mirror of https://github.com/stijndcl/didier
Merge pull request #105 from stijndcl/pycord-port
Port to Pycord & add newly supported featurespull/106/head
commit
eafc1a8674
|
@ -9,3 +9,4 @@ files/ufora_notifications.json
|
||||||
__pycache__
|
__pycache__
|
||||||
.env
|
.env
|
||||||
/venv/
|
/venv/
|
||||||
|
.pytest_cache
|
|
@ -1,82 +0,0 @@
|
||||||
from discord.ext import ipc
|
|
||||||
from functions.database import custom_commands
|
|
||||||
import json
|
|
||||||
from quart import Quart, jsonify, request
|
|
||||||
from quart_cors import cors
|
|
||||||
from time import time
|
|
||||||
|
|
||||||
|
|
||||||
app = Quart(__name__)
|
|
||||||
# TODO allow_origin=re.compile(r"http://localhost:.*")
|
|
||||||
# needs higher Python & Quart version
|
|
||||||
app = cors(app, allow_origin="*")
|
|
||||||
app.config.from_object(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
ipc_client = ipc.Client(secret_key="SOME_SECRET_KEY")
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/ping", methods=["GET"])
|
|
||||||
async def ping():
|
|
||||||
"""
|
|
||||||
Send a ping request, monitors bot latency and endpoint time
|
|
||||||
"""
|
|
||||||
latency = await ipc_client.request("get_bot_latency")
|
|
||||||
|
|
||||||
return jsonify({"bot_latency": latency, "response_sent": time()})
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/dm", methods=["POST"])
|
|
||||||
async def send_dm():
|
|
||||||
"""
|
|
||||||
Send a DM to the given user
|
|
||||||
"""
|
|
||||||
data = json.loads((await request.body).decode('UTF-8'))
|
|
||||||
|
|
||||||
dm = await ipc_client.request(
|
|
||||||
"send_dm",
|
|
||||||
user=int(data["userid"]),
|
|
||||||
message=data.get("message")
|
|
||||||
)
|
|
||||||
|
|
||||||
return jsonify({"response": dm})
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/custom", methods=["GET"])
|
|
||||||
async def get_all_custom_commands():
|
|
||||||
"""
|
|
||||||
Return a list of all custom commands in the bot
|
|
||||||
"""
|
|
||||||
commands = custom_commands.get_all()
|
|
||||||
|
|
||||||
return jsonify(commands)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/custom/<command_id>")
|
|
||||||
async def get_custom_command(command_id):
|
|
||||||
try:
|
|
||||||
command_id = int(command_id)
|
|
||||||
except ValueError:
|
|
||||||
# Id is not an int
|
|
||||||
return unprocessable_entity("Parameter id was not a valid integer.")
|
|
||||||
|
|
||||||
command = custom_commands.get_by_id(command_id)
|
|
||||||
|
|
||||||
if command is None:
|
|
||||||
return page_not_found("")
|
|
||||||
|
|
||||||
return jsonify(command)
|
|
||||||
|
|
||||||
|
|
||||||
@app.errorhandler(404)
|
|
||||||
def page_not_found(e):
|
|
||||||
return jsonify({"error": "No resource could be found matching the given URL."}), 404
|
|
||||||
|
|
||||||
|
|
||||||
@app.errorhandler(422)
|
|
||||||
def unprocessable_entity(e):
|
|
||||||
return jsonify({"error": e}), 422
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
app.run()
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
import discord
|
||||||
|
from discord import ApplicationContext
|
||||||
|
from discord.ext import commands
|
||||||
|
from discord.commands import message_command
|
||||||
|
|
||||||
|
from startup.didier import Didier
|
||||||
|
|
||||||
|
|
||||||
|
class SchoolCM(commands.Cog):
|
||||||
|
def __init__(self, client: Didier):
|
||||||
|
self.client: Didier = client
|
||||||
|
|
||||||
|
@message_command(name="Pin")
|
||||||
|
async def _pin_cm(self, ctx: ApplicationContext, message: discord.Message):
|
||||||
|
# In case people abuse, check if they're blacklisted
|
||||||
|
blacklist = []
|
||||||
|
|
||||||
|
if ctx.user.id in blacklist:
|
||||||
|
return await ctx.respond(":angry:", ephemeral=True)
|
||||||
|
|
||||||
|
if message.is_system():
|
||||||
|
return await ctx.respond("Dus jij wil system messages pinnen?\nMag niet.", ephemeral=True)
|
||||||
|
|
||||||
|
await message.pin(reason=f"Didier Pin door {ctx.user.display_name}")
|
||||||
|
await ctx.respond("📌", ephemeral=True)
|
||||||
|
|
||||||
|
|
||||||
|
def setup(client: Didier):
|
||||||
|
# client.add_cog(SchoolCM(client))
|
||||||
|
# TODO wait for bug to be fixed in lib then uncomment this
|
||||||
|
# when used in dm, tries to create a DM with the bot?
|
||||||
|
pass
|
|
@ -1,11 +1,10 @@
|
||||||
from dislash import SlashInteraction
|
from discord import Interaction
|
||||||
|
|
||||||
from data import constants
|
from data import constants
|
||||||
from data.snipe import Snipe, Action, should_snipe
|
from data.snipe import Snipe, Action, should_snipe
|
||||||
import datetime
|
import datetime
|
||||||
import discord
|
import discord
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from dislash.application_commands.errors import InteractionCheckFailure
|
|
||||||
from functions import checks, easterEggResponses, stringFormatters
|
from functions import checks, easterEggResponses, stringFormatters
|
||||||
from functions.database import stats, muttn, custom_commands, commands as command_stats
|
from functions.database import stats, muttn, custom_commands, commands as command_stats
|
||||||
import pytz
|
import pytz
|
||||||
|
@ -79,6 +78,12 @@ class Events(commands.Cog):
|
||||||
# Earn XP & Message count
|
# Earn XP & Message count
|
||||||
stats.sentMessage(message)
|
stats.sentMessage(message)
|
||||||
|
|
||||||
|
@commands.Cog.listener()
|
||||||
|
async def on_thread_join(self, thread: discord.Thread):
|
||||||
|
# Join threads automatically
|
||||||
|
if thread.me is None:
|
||||||
|
await thread.join()
|
||||||
|
|
||||||
@commands.Cog.listener()
|
@commands.Cog.listener()
|
||||||
async def on_command(self, ctx):
|
async def on_command(self, ctx):
|
||||||
"""
|
"""
|
||||||
|
@ -104,6 +109,7 @@ class Events(commands.Cog):
|
||||||
# Don't handle commands that have their own custom error handler
|
# Don't handle commands that have their own custom error handler
|
||||||
if hasattr(ctx.command, 'on_error'):
|
if hasattr(ctx.command, 'on_error'):
|
||||||
return
|
return
|
||||||
|
|
||||||
# Someone just mentioned Didier without calling a real command,
|
# Someone just mentioned Didier without calling a real command,
|
||||||
# don't care about this error
|
# don't care about this error
|
||||||
if isinstance(err, (commands.CommandNotFound, commands.CheckFailure, commands.TooManyArguments, commands.ExpectedClosingQuoteError), ):
|
if isinstance(err, (commands.CommandNotFound, commands.CheckFailure, commands.TooManyArguments, commands.ExpectedClosingQuoteError), ):
|
||||||
|
@ -115,32 +121,37 @@ class Events(commands.Cog):
|
||||||
await ctx.send("Geen message gevonden die overeenkomt met het opgegeven argument.")
|
await ctx.send("Geen message gevonden die overeenkomt met het opgegeven argument.")
|
||||||
elif isinstance(err, (commands.ChannelNotFound, commands.ChannelNotReadable)):
|
elif isinstance(err, (commands.ChannelNotFound, commands.ChannelNotReadable)):
|
||||||
await ctx.send("Geen channel gevonden dat overeenkomt met het opgegeven argument.")
|
await ctx.send("Geen channel gevonden dat overeenkomt met het opgegeven argument.")
|
||||||
|
elif isinstance(err, commands.ThreadNotFound):
|
||||||
|
await ctx.reply("Thread niet gevonden.", mention_author=False)
|
||||||
# Someone forgot an argument or passed an invalid argument
|
# Someone forgot an argument or passed an invalid argument
|
||||||
elif isinstance(err, (commands.BadArgument, commands.MissingRequiredArgument, commands.UnexpectedQuoteError)):
|
elif isinstance(err, (commands.BadArgument, commands.MissingRequiredArgument, commands.UnexpectedQuoteError)):
|
||||||
await ctx.send("Controleer je argumenten.")
|
await ctx.reply("Controleer je argumenten.", mention_author=False)
|
||||||
else:
|
else:
|
||||||
usage = stringFormatters.format_command_usage(ctx)
|
usage = stringFormatters.format_command_usage(ctx)
|
||||||
await self.sendErrorEmbed(err, "Command", usage)
|
await self.sendErrorEmbed(err, "Command", usage)
|
||||||
|
|
||||||
@commands.Cog.listener()
|
@commands.Cog.listener()
|
||||||
async def on_slash_command(self, interaction: SlashInteraction):
|
async def on_interaction(self, interaction: Interaction):
|
||||||
"""
|
"""
|
||||||
Function called whenever someone uses a slash command
|
Function called whenever someone uses a slash command
|
||||||
"""
|
"""
|
||||||
|
if not interaction.is_command():
|
||||||
|
return
|
||||||
|
|
||||||
print(stringFormatters.format_slash_command_usage(interaction))
|
print(stringFormatters.format_slash_command_usage(interaction))
|
||||||
|
|
||||||
command_stats.invoked(command_stats.InvocationType.SlashCommand)
|
command_stats.invoked(command_stats.InvocationType.SlashCommand)
|
||||||
|
|
||||||
@commands.Cog.listener()
|
@commands.Cog.listener()
|
||||||
async def on_slash_command_error(self, interaction, err):
|
async def on_application_command_error(self, ctx: discord.ApplicationContext, err):
|
||||||
# Debugging Didier shouldn't spam the error logs
|
# Debugging Didier shouldn't spam the error logs
|
||||||
if self.client.user.id != int(constants.didierId):
|
if self.client.user.id != int(constants.didierId):
|
||||||
raise err
|
raise err
|
||||||
|
|
||||||
if isinstance(err, InteractionCheckFailure):
|
if isinstance(err, commands.CheckFailure):
|
||||||
return await interaction.reply("Je hebt geen toegang tot dit commando.", ephemeral=True)
|
return await ctx.respond("Je hebt geen toegang tot dit commando.", ephemeral=True)
|
||||||
|
|
||||||
usage = stringFormatters.format_slash_command_usage(interaction)
|
usage = stringFormatters.format_slash_command_usage(ctx.interaction)
|
||||||
await self.sendErrorEmbed(err, "Slash Command", usage)
|
await self.sendErrorEmbed(err, "Slash Command", usage)
|
||||||
|
|
||||||
@commands.Cog.listener()
|
@commands.Cog.listener()
|
||||||
|
|
38
cogs/fun.py
38
cogs/fun.py
|
@ -1,15 +1,16 @@
|
||||||
from data.embeds.xkcd import XKCDEmbed
|
|
||||||
from data.menus import paginatedLeaderboard
|
|
||||||
from decorators import help
|
|
||||||
import discord
|
|
||||||
from discord.ext import commands
|
|
||||||
from enums.help_categories import Category
|
|
||||||
from functions import checks, stringFormatters
|
|
||||||
from functions.database import memes, trump, dadjoke
|
|
||||||
from functions.memes import generate
|
|
||||||
import json
|
import json
|
||||||
import random
|
import random
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
from discord.ext import commands
|
||||||
|
|
||||||
|
from data.embeds.xkcd import XKCDEmbed
|
||||||
|
from data.menus.memes import MemesList
|
||||||
|
from decorators import help
|
||||||
|
from enums.help_categories import Category
|
||||||
|
from functions import checks
|
||||||
|
from functions.database import memes, trump, dadjoke
|
||||||
|
from functions.memes import generate
|
||||||
|
|
||||||
|
|
||||||
class Fun(commands.Cog):
|
class Fun(commands.Cog):
|
||||||
|
@ -98,17 +99,10 @@ class Fun(commands.Cog):
|
||||||
if result is None:
|
if result is None:
|
||||||
return await ctx.send("Deze meme staat niet in de database.")
|
return await ctx.send("Deze meme staat niet in de database.")
|
||||||
|
|
||||||
# Convert to list to support item assignment
|
|
||||||
fields = list(fields)
|
|
||||||
|
|
||||||
generated = generate(result, fields)
|
generated = generate(result, fields)
|
||||||
|
|
||||||
# If the request was successful, remove the message calling it
|
|
||||||
if generated["success"]:
|
|
||||||
await self.utilsCog.removeMessage(ctx.message)
|
|
||||||
|
|
||||||
# Send the meme's url or the error message
|
# Send the meme's url or the error message
|
||||||
await ctx.send(generated["message"])
|
await ctx.reply(generated["message"], mention_author=False)
|
||||||
|
|
||||||
@commands.command(name="Memes")
|
@commands.command(name="Memes")
|
||||||
@commands.check(checks.allowedChannels)
|
@commands.check(checks.allowedChannels)
|
||||||
|
@ -118,15 +112,7 @@ class Fun(commands.Cog):
|
||||||
Command that shows a list of memes in the database.
|
Command that shows a list of memes in the database.
|
||||||
:param ctx: Discord Context
|
:param ctx: Discord Context
|
||||||
"""
|
"""
|
||||||
memeList = memes.getAllMemes()
|
return await MemesList(ctx=ctx).send()
|
||||||
|
|
||||||
# Turn the list into a list of [Name: fields]
|
|
||||||
memeList = [": ".join([stringFormatters.title_case(meme[1]),
|
|
||||||
str(meme[2])]) for meme in sorted(memeList, key=lambda x: x[1])]
|
|
||||||
|
|
||||||
pages = paginatedLeaderboard.Pages(source=paginatedLeaderboard.Source(memeList, "Memes", discord.Colour.blue()),
|
|
||||||
clear_reactions_after=True)
|
|
||||||
await pages.start(ctx)
|
|
||||||
|
|
||||||
@commands.command(name="Pjoke")
|
@commands.command(name="Pjoke")
|
||||||
@help.Category(category=Category.Fun)
|
@help.Category(category=Category.Fun)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from data import constants
|
from data import constants
|
||||||
import discord
|
import discord
|
||||||
|
from discord.commands import SlashCommand
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from enums.help_categories import categories, getCategory, Category
|
from enums.help_categories import categories, getCategory, Category
|
||||||
import json
|
import json
|
||||||
|
@ -49,7 +50,7 @@ class HelpCommand(commands.MinimalHelpCommand):
|
||||||
return await self.send_bot_help(self.get_bot_mapping())
|
return await self.send_bot_help(self.get_bot_mapping())
|
||||||
|
|
||||||
# Turn dic to lowercase to allow proper name searching
|
# Turn dic to lowercase to allow proper name searching
|
||||||
all_commands = dict((k.lower(), v) for k, v in bot.all_commands.items())
|
all_commands = dict((k.lower(), v) for k, v in bot.all_commands.items() if not isinstance(v, SlashCommand))
|
||||||
|
|
||||||
if spl[0].lower() not in all_commands:
|
if spl[0].lower() not in all_commands:
|
||||||
return await self.send_error_message(await self.command_not_found(spl[0]))
|
return await self.send_error_message(await self.command_not_found(spl[0]))
|
||||||
|
|
26
cogs/ipc.py
26
cogs/ipc.py
|
@ -1,26 +0,0 @@
|
||||||
from discord.ext import commands, ipc
|
|
||||||
|
|
||||||
|
|
||||||
class IPC(commands.Cog):
|
|
||||||
def __init__(self, client):
|
|
||||||
self.client = client
|
|
||||||
|
|
||||||
@ipc.server.route()
|
|
||||||
async def send_dm(self, data):
|
|
||||||
print("got here")
|
|
||||||
user = self.client.get_user(data.user)
|
|
||||||
await user.send(data.message)
|
|
||||||
print("sent")
|
|
||||||
return True
|
|
||||||
|
|
||||||
@ipc.server.route()
|
|
||||||
async def get_bot_latency(self, data):
|
|
||||||
"""
|
|
||||||
Get Didier's latency
|
|
||||||
"""
|
|
||||||
|
|
||||||
return self.client.latency * 1000
|
|
||||||
|
|
||||||
|
|
||||||
def setup(client):
|
|
||||||
client.add_cog(IPC(client))
|
|
|
@ -1,16 +1,12 @@
|
||||||
from data.menus import paginatedLeaderboard
|
|
||||||
from decorators import help
|
|
||||||
import discord
|
import discord
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
|
|
||||||
|
from data.menus import leaderboards
|
||||||
|
from decorators import help
|
||||||
from enums.help_categories import Category
|
from enums.help_categories import Category
|
||||||
from enums.numbers import Numbers
|
from functions import checks
|
||||||
from functions import checks, xp
|
|
||||||
from functions.database import currency, stats, poke, muttn
|
|
||||||
import math
|
|
||||||
import requests
|
|
||||||
|
|
||||||
|
|
||||||
# TODO some sort of general leaderboard because all of them are the same
|
|
||||||
class Leaderboards(commands.Cog):
|
class Leaderboards(commands.Cog):
|
||||||
|
|
||||||
def __init__(self, client):
|
def __init__(self, client):
|
||||||
|
@ -18,7 +14,7 @@ class Leaderboards(commands.Cog):
|
||||||
self.utilsCog = self.client.get_cog("Utils")
|
self.utilsCog = self.client.get_cog("Utils")
|
||||||
|
|
||||||
# Don't allow any commands to work when locked
|
# Don't allow any commands to work when locked
|
||||||
def cog_check(self, ctx):
|
def cog_check(self, _):
|
||||||
return not self.client.locked
|
return not self.client.locked
|
||||||
|
|
||||||
@commands.group(name="Leaderboard", aliases=["Lb", "Leaderboards"], case_insensitive=True, usage="[Categorie]*",
|
@commands.group(name="Leaderboard", aliases=["Lb", "Leaderboards"], case_insensitive=True, usage="[Categorie]*",
|
||||||
|
@ -34,174 +30,47 @@ class Leaderboards(commands.Cog):
|
||||||
|
|
||||||
@leaderboard.command(name="Dinks", aliases=["Cash"], hidden=True)
|
@leaderboard.command(name="Dinks", aliases=["Cash"], hidden=True)
|
||||||
async def dinks(self, ctx):
|
async def dinks(self, ctx):
|
||||||
entries = currency.getAllRows()
|
lb = leaderboards.DinksLeaderboard(ctx=ctx)
|
||||||
platDinks = currency.getAllPlatDinks()
|
await lb.send()
|
||||||
|
|
||||||
# Take platinum dinks into account
|
|
||||||
for i, user in enumerate(entries):
|
|
||||||
if str(user[0]) in platDinks:
|
|
||||||
# Tuples don't support assignment, cast to list
|
|
||||||
user = list(user)
|
|
||||||
user[1] += platDinks[str(user[0])] * Numbers.q.value
|
|
||||||
entries[i] = user
|
|
||||||
|
|
||||||
boardTop = []
|
|
||||||
for i, user in enumerate(sorted(entries, key=lambda x: (float(x[1]) + float(x[3])), reverse=True)):
|
|
||||||
if i == 0 and float(user[1]) + float(user[3]) == 0.0:
|
|
||||||
return await self.emptyLeaderboard(ctx, "Dinks Leaderboard", "Er zijn nog geen personen met Didier Dinks.")
|
|
||||||
elif float(user[1]) + float(user[3]) > 0.0:
|
|
||||||
|
|
||||||
# Get the username in this guild
|
|
||||||
name = self.utilsCog.getDisplayName(ctx, user[0])
|
|
||||||
|
|
||||||
if int(user[0]) == int(ctx.author.id):
|
|
||||||
boardTop.append("**{} ({:,})**".format(name, math.floor(float(user[1]) + float(user[3]))))
|
|
||||||
else:
|
|
||||||
boardTop.append("{} ({:,})".format(name, math.floor(float(user[1]) + float(user[3]))))
|
|
||||||
|
|
||||||
await self.startPaginated(ctx, boardTop, "Dinks Leaderboard")
|
|
||||||
|
|
||||||
@leaderboard.command(name="Corona", hidden=True)
|
@leaderboard.command(name="Corona", hidden=True)
|
||||||
async def corona(self, ctx):
|
async def corona(self, ctx):
|
||||||
result = requests.get("http://corona.lmao.ninja/v2/countries").json()
|
lb = leaderboards.CoronaLeaderboard(ctx=ctx)
|
||||||
result.sort(key=lambda x: int(x["cases"]), reverse=True)
|
await lb.send()
|
||||||
board = []
|
|
||||||
for land in result:
|
|
||||||
|
|
||||||
if land["country"] == "Belgium":
|
|
||||||
board.append("**{} ({:,})**".format(land["country"], land["cases"]))
|
|
||||||
else:
|
|
||||||
board.append("{} ({:,})".format(land["country"], land["cases"]))
|
|
||||||
|
|
||||||
await self.startPaginated(ctx, board, "Corona Leaderboard", discord.Colour.red())
|
|
||||||
|
|
||||||
@leaderboard.command(name="Bitcoin", aliases=["Bc"], hidden=True)
|
@leaderboard.command(name="Bitcoin", aliases=["Bc"], hidden=True)
|
||||||
async def bitcoin(self, ctx):
|
async def bitcoin(self, ctx):
|
||||||
users = currency.getAllRows()
|
lb = leaderboards.BitcoinLeaderboard(ctx=ctx)
|
||||||
boardTop = []
|
await lb.send()
|
||||||
for i, user in enumerate(sorted(users, key=lambda x: x[8], reverse=True)):
|
|
||||||
# Don't create an empty leaderboard
|
|
||||||
if i == 0 and float(user[8]) == 0.0:
|
|
||||||
return await self.emptyLeaderboard(ctx, "Bitcoin Leaderboard", "Er zijn nog geen personen met Bitcoins.")
|
|
||||||
elif float(user[8]) > 0.0:
|
|
||||||
# Only add people with more than 0
|
|
||||||
# Get the username in this guild
|
|
||||||
name = self.utilsCog.getDisplayName(ctx, user[0])
|
|
||||||
if int(user[0]) == int(ctx.author.id):
|
|
||||||
boardTop.append("**{} ({:,})**".format(name, round(user[8], 8)))
|
|
||||||
else:
|
|
||||||
boardTop.append("{} ({:,})".format(name, round(user[8], 8)))
|
|
||||||
|
|
||||||
await self.startPaginated(ctx, boardTop, "Bitcoin Leaderboard")
|
|
||||||
|
|
||||||
@leaderboard.command(name="Rob", hidden=True)
|
@leaderboard.command(name="Rob", hidden=True)
|
||||||
async def rob(self, ctx):
|
async def rob(self, ctx):
|
||||||
users = list(stats.getAllRows())
|
lb = leaderboards.RobLeaderboard(ctx=ctx)
|
||||||
boardTop = []
|
await lb.send()
|
||||||
for i, user in enumerate(sorted(users, key=lambda x: x[4], reverse=True)):
|
|
||||||
# Don't create an empty leaderboard
|
|
||||||
if i == 0 and float(user[4]) == 0.0:
|
|
||||||
return await self.emptyLeaderboard(ctx, "Rob Leaderboard", "Er heeft nog niemand Didier Dinks gestolen.")
|
|
||||||
elif float(user[4]) > 0.0:
|
|
||||||
# Only add people with more than 0
|
|
||||||
# Get the username in this guild
|
|
||||||
name = self.utilsCog.getDisplayName(ctx, user[0])
|
|
||||||
if int(user[0]) == int(ctx.author.id):
|
|
||||||
boardTop.append("**{} ({:,})**".format(name, math.floor(float(user[4]))))
|
|
||||||
else:
|
|
||||||
boardTop.append("{} ({:,})".format(name, math.floor(float(user[4]))))
|
|
||||||
await self.startPaginated(ctx, boardTop, "Rob Leaderboard")
|
|
||||||
|
|
||||||
@leaderboard.command(name="Poke", hidden=True)
|
@leaderboard.command(name="Poke", hidden=True)
|
||||||
async def poke(self, ctx):
|
async def poke(self, ctx):
|
||||||
s = stats.getAllRows()
|
lb = leaderboards.PokeLeaderboard(ctx=ctx)
|
||||||
blacklist = poke.getAllBlacklistedUsers()
|
await lb.send()
|
||||||
boardTop = []
|
|
||||||
for i, user in enumerate(sorted(s, key=lambda x: x[1], reverse=True)):
|
|
||||||
if i == 0 and int(user[1]) == 0:
|
|
||||||
return await self.emptyLeaderboard(ctx, "Poke Leaderboard", "Er is nog niemand getikt.")
|
|
||||||
|
|
||||||
elif int(user[1]) == 0:
|
|
||||||
break
|
|
||||||
# Don't include blacklisted users
|
|
||||||
elif str(user[0]) not in blacklist:
|
|
||||||
name = self.utilsCog.getDisplayName(ctx, user[0])
|
|
||||||
if int(user[0]) == int(ctx.author.id):
|
|
||||||
boardTop.append("**{} ({:,})**".format(name, round(int(user[1]))))
|
|
||||||
else:
|
|
||||||
boardTop.append("{} ({:,})".format(name, round(int(user[1]))))
|
|
||||||
await self.startPaginated(ctx, boardTop, "Poke Leaderboard")
|
|
||||||
|
|
||||||
@leaderboard.command(name="Xp", aliases=["Level"], hidden=True)
|
@leaderboard.command(name="Xp", aliases=["Level"], hidden=True)
|
||||||
async def xp(self, ctx):
|
async def xp(self, ctx):
|
||||||
s = stats.getAllRows()
|
lb = leaderboards.XPLeaderboard(ctx=ctx)
|
||||||
boardTop = []
|
await lb.send()
|
||||||
for i, user in enumerate(sorted(s, key=lambda x: x[12], reverse=True)):
|
|
||||||
if int(user[12]) == 0:
|
|
||||||
break
|
|
||||||
|
|
||||||
name = self.utilsCog.getDisplayName(ctx, user[0])
|
|
||||||
if int(user[0]) == int(ctx.author.id):
|
|
||||||
boardTop.append("**{} (Level {:,} | {:,} XP)**".format(name,
|
|
||||||
xp.calculate_level(round(int(user[12]))),
|
|
||||||
round(int(user[12]))))
|
|
||||||
else:
|
|
||||||
boardTop.append("{} (Level {:,} | {:,} XP)".format(name,
|
|
||||||
xp.calculate_level(round(int(user[12]))),
|
|
||||||
round(int(user[12]))))
|
|
||||||
await self.startPaginated(ctx, boardTop, "XP Leaderboard")
|
|
||||||
|
|
||||||
@leaderboard.command(name="Messages", aliases=["Mc", "Mess"], hidden=True)
|
@leaderboard.command(name="Messages", aliases=["Mc", "Mess"], hidden=True)
|
||||||
async def messages(self, ctx):
|
async def messages(self, ctx):
|
||||||
s = stats.getAllRows()
|
lb = leaderboards.MessageLeaderboard(ctx=ctx)
|
||||||
boardTop = []
|
await lb.send()
|
||||||
|
|
||||||
message_count = stats.getTotalMessageCount()
|
|
||||||
|
|
||||||
for i, user in enumerate(sorted(s, key=lambda x: x[11], reverse=True)):
|
|
||||||
if int(user[11]) == 0:
|
|
||||||
break
|
|
||||||
|
|
||||||
perc = round(int(user[11]) * 100 / message_count, 2)
|
|
||||||
|
|
||||||
name = self.utilsCog.getDisplayName(ctx, user[0])
|
|
||||||
if int(user[0]) == int(ctx.author.id):
|
|
||||||
boardTop.append("**{} ({:,} | {}%)**".format(name, round(int(user[11])), perc))
|
|
||||||
else:
|
|
||||||
boardTop.append("{} ({:,} | {}%)".format(name, round(int(user[11])), perc))
|
|
||||||
await self.startPaginated(ctx, boardTop, "Messages Leaderboard")
|
|
||||||
|
|
||||||
@leaderboard.command(name="Muttn", aliases=["M", "Mutn", "Mutten"], hidden=True)
|
@leaderboard.command(name="Muttn", aliases=["M", "Mutn", "Mutten"], hidden=True)
|
||||||
async def muttn(self, ctx):
|
async def muttn(self, ctx):
|
||||||
users = muttn.getAllRows()
|
lb = leaderboards.MuttnLeaderboard(ctx=ctx)
|
||||||
boardTop = []
|
await lb.send()
|
||||||
for i, user in enumerate(sorted(users, key=lambda x: x[1], reverse=True)):
|
|
||||||
if i == 0 and int(user[1]) == 0:
|
|
||||||
return await self.emptyLeaderboard(ctx, "Muttn Leaderboard", "Der zittn nog geen muttns in de server.")
|
|
||||||
|
|
||||||
if float(user[1]) == 0:
|
|
||||||
break
|
|
||||||
|
|
||||||
name = self.utilsCog.getDisplayName(ctx, user[0])
|
|
||||||
if int(user[0]) == int(ctx.author.id):
|
|
||||||
boardTop.append("**{} ({})%**".format(name, round(float(user[1]), 2)))
|
|
||||||
else:
|
|
||||||
boardTop.append("{} ({}%)".format(name, round(float(user[1]), 2)))
|
|
||||||
await self.startPaginated(ctx, boardTop, "Muttn Leaderboard")
|
|
||||||
|
|
||||||
async def callLeaderboard(self, name, ctx):
|
async def callLeaderboard(self, name, ctx):
|
||||||
await [command for command in self.leaderboard.commands if command.name.lower() == name.lower()][0](ctx)
|
command = [command for command in self.leaderboard.commands if command.name.lower() == name.lower()][0]
|
||||||
|
await command(ctx)
|
||||||
async def startPaginated(self, ctx, source, name, colour=discord.Colour.blue()):
|
|
||||||
pages = paginatedLeaderboard.Pages(source=paginatedLeaderboard.Source(source, name, colour),
|
|
||||||
clear_reactions_after=True)
|
|
||||||
await pages.start(ctx)
|
|
||||||
|
|
||||||
async def emptyLeaderboard(self, ctx, name, message, colour=discord.Colour.blue()):
|
|
||||||
embed = discord.Embed(colour=colour)
|
|
||||||
embed.set_author(name=name)
|
|
||||||
embed.description = message
|
|
||||||
await ctx.send(embed=embed)
|
|
||||||
|
|
||||||
|
|
||||||
def setup(client):
|
def setup(client):
|
||||||
|
|
|
@ -151,7 +151,7 @@ class Oneliners(commands.Cog):
|
||||||
@commands.command(name="Inspire")
|
@commands.command(name="Inspire")
|
||||||
@help.Category(Category.Other)
|
@help.Category(Category.Other)
|
||||||
async def inspire(self, ctx):
|
async def inspire(self, ctx):
|
||||||
image = get("http://inspirobot.me/api?generate=true")
|
image = get("https://inspirobot.me/api?generate=true")
|
||||||
|
|
||||||
if image.status_code == 200:
|
if image.status_code == 200:
|
||||||
await ctx.send(image.text)
|
await ctx.send(image.text)
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
|
import discord
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
|
|
||||||
from data.embeds.snipe import EditSnipe, DeleteSnipe
|
from data.embeds.snipe import EditSnipe, DeleteSnipe
|
||||||
|
from data.links import get_link_for
|
||||||
from data.menus import custom_commands
|
from data.menus import custom_commands
|
||||||
from data.snipe import Action, Snipe
|
from data.snipe import Action, Snipe
|
||||||
from decorators import help
|
from decorators import help
|
||||||
from enums.help_categories import Category
|
from enums.help_categories import Category
|
||||||
from functions.database.custom_commands import get_all
|
from functions.utils import reply_to_reference
|
||||||
from functions.stringFormatters import capitalize
|
|
||||||
from startup.didier import Didier
|
from startup.didier import Didier
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,10 +15,22 @@ class Other(commands.Cog):
|
||||||
def __init__(self, client: Didier):
|
def __init__(self, client: Didier):
|
||||||
self.client: Didier = client
|
self.client: Didier = client
|
||||||
|
|
||||||
# TODO add locked field to Didier instead of client
|
# Don't allow any commands to work when locked
|
||||||
# # Don't allow any commands to work when locked
|
def cog_check(self, _):
|
||||||
# def cog_check(self, ctx):
|
return not self.client.locked
|
||||||
# return not self.client.locked
|
|
||||||
|
@commands.command(name="Link", usage="[Naam]")
|
||||||
|
@help.Category(category=Category.Other)
|
||||||
|
async def link(self, ctx: commands.Context, name: str):
|
||||||
|
"""
|
||||||
|
Send commonly used links
|
||||||
|
"""
|
||||||
|
match = get_link_for(name)
|
||||||
|
|
||||||
|
if match is None:
|
||||||
|
return await ctx.reply(f"Geen match gevonden voor \"{name}\".", mention_author=False, delete_after=15)
|
||||||
|
|
||||||
|
await reply_to_reference(ctx, content=match)
|
||||||
|
|
||||||
@commands.command(name="Custom")
|
@commands.command(name="Custom")
|
||||||
@help.Category(category=Category.Didier)
|
@help.Category(category=Category.Didier)
|
||||||
|
@ -25,10 +38,17 @@ class Other(commands.Cog):
|
||||||
"""
|
"""
|
||||||
Get a list of all custom commands
|
Get a list of all custom commands
|
||||||
"""
|
"""
|
||||||
all_commands = get_all()
|
await custom_commands.CommandsList(ctx).send()
|
||||||
formatted = list(sorted(map(lambda x: capitalize(x["name"]), all_commands)))
|
|
||||||
src = custom_commands.CommandsList(formatted)
|
@commands.command(name="Join", usage="[Thread]")
|
||||||
await custom_commands.Pages(source=src, clear_reactions_after=True).start(ctx)
|
@help.Category(category=Category.Didier)
|
||||||
|
async def join_thread(self, ctx, thread: discord.Thread):
|
||||||
|
"""
|
||||||
|
Join threads
|
||||||
|
"""
|
||||||
|
if thread.me is None:
|
||||||
|
await thread.join()
|
||||||
|
await ctx.message.add_reaction("✅")
|
||||||
|
|
||||||
@commands.command(name="Snipe")
|
@commands.command(name="Snipe")
|
||||||
@help.Category(category=Category.Other)
|
@help.Category(category=Category.Other)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from data import schedule
|
from data import schedule
|
||||||
|
from data.courses import find_course_from_name
|
||||||
from data.embeds.deadlines import Deadlines
|
from data.embeds.deadlines import Deadlines
|
||||||
from data.embeds.food import Menu
|
from data.embeds.food import Menu
|
||||||
from decorators import help
|
from decorators import help
|
||||||
|
@ -8,6 +9,7 @@ from enums.help_categories import Category
|
||||||
from functions import config, les
|
from functions import config, les
|
||||||
from functions.stringFormatters import capitalize
|
from functions.stringFormatters import capitalize
|
||||||
from functions.timeFormatters import skip_weekends
|
from functions.timeFormatters import skip_weekends
|
||||||
|
from functions.utils import reply_to_reference
|
||||||
|
|
||||||
|
|
||||||
class School(commands.Cog):
|
class School(commands.Cog):
|
||||||
|
@ -82,9 +84,28 @@ class School(commands.Cog):
|
||||||
if message.is_system():
|
if message.is_system():
|
||||||
return await ctx.send("Dus jij wil system messages pinnen?\nMag niet.")
|
return await ctx.send("Dus jij wil system messages pinnen?\nMag niet.")
|
||||||
|
|
||||||
await message.pin(reason="Didier Pin door {}".format(ctx.author.display_name))
|
await message.pin(reason=f"Didier Pin door {ctx.author.display_name}")
|
||||||
await ctx.message.add_reaction("✅")
|
await ctx.message.add_reaction("✅")
|
||||||
|
|
||||||
|
@commands.command(name="Fiche", usage="[Vak]", aliases=["guide", "studiefiche"])
|
||||||
|
@help.Category(category=Category.School)
|
||||||
|
async def study_guide(self, ctx, name: str):
|
||||||
|
"""
|
||||||
|
Send links to study guides
|
||||||
|
"""
|
||||||
|
# Find code corresponding to the search query
|
||||||
|
course = find_course_from_name(name)
|
||||||
|
|
||||||
|
# Code not found
|
||||||
|
if course is None:
|
||||||
|
return await ctx.reply(f"Onbekend vak: \"{name}\".", mention_author=False, delete_after=15)
|
||||||
|
|
||||||
|
# Get the guide for the current year
|
||||||
|
year = 2018 + int(config.get("year"))
|
||||||
|
link = f"https://studiekiezer.ugent.be/studiefiche/nl/{course.code}/{year}"
|
||||||
|
|
||||||
|
return await reply_to_reference(ctx, content=link)
|
||||||
|
|
||||||
@commands.command(name="Deadlines", aliases=["dl"])
|
@commands.command(name="Deadlines", aliases=["dl"])
|
||||||
@help.Category(category=Category.School)
|
@help.Category(category=Category.School)
|
||||||
async def deadlines(self, ctx):
|
async def deadlines(self, ctx):
|
||||||
|
|
|
@ -1,87 +0,0 @@
|
||||||
import datetime
|
|
||||||
import json
|
|
||||||
|
|
||||||
from discord.ext import commands
|
|
||||||
from dislash import SlashInteraction, slash_command, Option, OptionType, check
|
|
||||||
from functions.checks import isMe
|
|
||||||
from functions.timeFormatters import fromString
|
|
||||||
from startup.didier import Didier
|
|
||||||
|
|
||||||
|
|
||||||
class DBSlash(commands.Cog):
|
|
||||||
def __init__(self, client: Didier):
|
|
||||||
self.client: Didier = client
|
|
||||||
|
|
||||||
@slash_command(name="db")
|
|
||||||
@check(isMe)
|
|
||||||
async def _db_slash(self, interaction: SlashInteraction):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@_db_slash.sub_command_group(name="add")
|
|
||||||
async def _add_slash(self, interaction: SlashInteraction):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@_add_slash.sub_command(
|
|
||||||
name="deadline",
|
|
||||||
options=[
|
|
||||||
Option(
|
|
||||||
"year",
|
|
||||||
description="Year (1-based)",
|
|
||||||
type=OptionType.INTEGER,
|
|
||||||
required=True
|
|
||||||
),
|
|
||||||
Option(
|
|
||||||
"course",
|
|
||||||
description="Course (abbreviated)",
|
|
||||||
type=OptionType.STRING,
|
|
||||||
required=True
|
|
||||||
),
|
|
||||||
Option(
|
|
||||||
"name",
|
|
||||||
description="Name of the deadline/project",
|
|
||||||
type=OptionType.STRING,
|
|
||||||
required=True
|
|
||||||
),
|
|
||||||
Option(
|
|
||||||
"date",
|
|
||||||
description="Date (DD/MM)",
|
|
||||||
type=OptionType.STRING,
|
|
||||||
required=True
|
|
||||||
),
|
|
||||||
Option(
|
|
||||||
"time",
|
|
||||||
description="Timestamp (HH:MM or HH:MM:SS)",
|
|
||||||
type=OptionType.STRING,
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
async def _add_deadline_slash(self, interaction: SlashInteraction, year: int, course: str, name: str, date: str, time: str = "00:00:00"):
|
|
||||||
with open("files/deadlines.json", "r") as f:
|
|
||||||
deadlines = json.load(f)
|
|
||||||
|
|
||||||
date += "/" + str(datetime.datetime.now().year)
|
|
||||||
|
|
||||||
# Fix format
|
|
||||||
if time.count(":") == 1:
|
|
||||||
time += ":00"
|
|
||||||
|
|
||||||
dt = fromString(f"{date} {time}", formatString="%d/%m/%Y %H:%M:%S", tzinfo=None)
|
|
||||||
|
|
||||||
# Add year & course if necessary
|
|
||||||
if str(year) not in deadlines:
|
|
||||||
deadlines[str(year)] = {}
|
|
||||||
|
|
||||||
if course not in deadlines[str(year)]:
|
|
||||||
deadlines[str(year)][course] = {}
|
|
||||||
|
|
||||||
deadlines[str(year)][course][name] = round(dt.timestamp())
|
|
||||||
|
|
||||||
with open("files/deadlines.json", "w") as f:
|
|
||||||
json.dump(deadlines, f)
|
|
||||||
|
|
||||||
await interaction.reply("Addition successful", ephemeral=True)
|
|
||||||
|
|
||||||
|
|
||||||
def setup(client: Didier):
|
|
||||||
client.add_cog(DBSlash(client))
|
|
|
@ -1,5 +1,5 @@
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from dislash import SlashInteraction, slash_command, Option, OptionType
|
from discord.commands import slash_command, ApplicationContext, Option
|
||||||
|
|
||||||
from data.embeds.urban_dictionary import Definition
|
from data.embeds.urban_dictionary import Definition
|
||||||
from startup.didier import Didier
|
from startup.didier import Didier
|
||||||
|
@ -9,15 +9,10 @@ class DefineSlash(commands.Cog):
|
||||||
def __init__(self, client: Didier):
|
def __init__(self, client: Didier):
|
||||||
self.client: Didier = client
|
self.client: Didier = client
|
||||||
|
|
||||||
@slash_command(name="define",
|
@slash_command(name="define", description="Urban Dictionary")
|
||||||
description="Urban Dictionary",
|
async def _define_slash(self, ctx: ApplicationContext, query: Option(str, "Search query", required=True)):
|
||||||
options=[
|
|
||||||
Option("query", "Search query", OptionType.STRING, required=True)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
async def _define_slash(self, interaction: SlashInteraction, query):
|
|
||||||
embed = Definition(query).to_embed()
|
embed = Definition(query).to_embed()
|
||||||
await interaction.reply(embed=embed)
|
await ctx.respond(embed=embed)
|
||||||
|
|
||||||
|
|
||||||
def setup(client: Didier):
|
def setup(client: Didier):
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from dislash import SlashInteraction, slash_command, Option, OptionType
|
from discord.commands import Option, SlashCommandGroup, ApplicationContext, permissions
|
||||||
from functions import config, checks
|
from functions import config
|
||||||
from functions.football import get_matches, get_table, get_jpl_code
|
from functions.football import get_matches, get_table, get_jpl_code
|
||||||
from startup.didier import Didier
|
from startup.didier import Didier
|
||||||
|
|
||||||
|
@ -9,35 +9,29 @@ class FootballSlash(commands.Cog):
|
||||||
def __init__(self, client: Didier):
|
def __init__(self, client: Didier):
|
||||||
self.client: Didier = client
|
self.client: Didier = client
|
||||||
|
|
||||||
@slash_command(name="jpl", description="Jupiler Pro League commands")
|
_jpl_group = SlashCommandGroup("jpl", "Jupiler Pro League commands")
|
||||||
async def _jpl_group(self, interaction: SlashInteraction):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@_jpl_group.sub_command(name="matches",
|
@_jpl_group.command(name="matches", description="Schema voor een bepaalde speeldag")
|
||||||
description="Schema voor een bepaalde speeldag",
|
async def _jpl_matches_slash(self, ctx: ApplicationContext,
|
||||||
options=[
|
day: Option(int, name="day", description="Speeldag (default huidige)", required=False, default=None)
|
||||||
Option("day", "Speeldag (default huidige)", OptionType.INTEGER)
|
):
|
||||||
]
|
|
||||||
)
|
|
||||||
async def _jpl_matches_slash(self, interaction: SlashInteraction, day: int = None):
|
|
||||||
# Default is current day
|
# Default is current day
|
||||||
if day is None:
|
if day is None:
|
||||||
day = int(config.get("jpl_day"))
|
day = int(config.get("jpl_day"))
|
||||||
|
|
||||||
await interaction.reply(get_matches(day))
|
await ctx.respond(get_matches(day))
|
||||||
|
|
||||||
@_jpl_group.sub_command(name="table", description="Huidige rangschikking")
|
@_jpl_group.command(name="table", description="Huidige rangschikking")
|
||||||
async def _jpl_table_slash(self, interaction: SlashInteraction):
|
async def _jpl_table_slash(self, ctx: ApplicationContext):
|
||||||
await interaction.reply(get_table())
|
await ctx.response.defer()
|
||||||
|
await ctx.send_followup(get_table())
|
||||||
@_jpl_group.sub_command(name="update", description="Update de code voor deze competitie (owner-only)")
|
|
||||||
async def _jpl_update_slash(self, interaction: SlashInteraction):
|
|
||||||
if not await checks.isMe(interaction):
|
|
||||||
return await interaction.reply(f"Je hebt geen toegang tot dit commando.")
|
|
||||||
|
|
||||||
|
@_jpl_group.command(name="update", description="Update de code voor deze competitie (owner-only)", default_permission=False)
|
||||||
|
@permissions.is_owner()
|
||||||
|
async def _jpl_update_slash(self, ctx: ApplicationContext):
|
||||||
code = get_jpl_code()
|
code = get_jpl_code()
|
||||||
config.config("jpl", code)
|
config.config("jpl", code)
|
||||||
await interaction.reply(f"Done (code: {code})")
|
await ctx.respond(f"Done (code: {code})")
|
||||||
|
|
||||||
|
|
||||||
def setup(client: Didier):
|
def setup(client: Didier):
|
||||||
|
|
|
@ -1,28 +1,69 @@
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from dislash import SlashInteraction, slash_command, Option, OptionType
|
from discord.commands import slash_command, ApplicationContext, Option, AutocompleteContext
|
||||||
|
|
||||||
|
from functions.database import memes
|
||||||
|
from functions.database.memes import getAllMemes
|
||||||
from data.embeds.xkcd import XKCDEmbed
|
from data.embeds.xkcd import XKCDEmbed
|
||||||
|
from data.menus.memes import MemesList
|
||||||
|
from functions.memes import generate
|
||||||
|
from functions.stringFormatters import title_case
|
||||||
from startup.didier import Didier
|
from startup.didier import Didier
|
||||||
|
|
||||||
|
|
||||||
|
all_memes = getAllMemes()
|
||||||
|
|
||||||
|
|
||||||
|
def autocomplete_memes(ctx: AutocompleteContext) -> list[str]:
|
||||||
|
starting = []
|
||||||
|
containing = []
|
||||||
|
|
||||||
|
val = ctx.value.lower()
|
||||||
|
|
||||||
|
# First show matches that start with this word, then matches that contain it
|
||||||
|
for meme in all_memes:
|
||||||
|
if meme[1].startswith(val):
|
||||||
|
starting.append(title_case(meme[1]))
|
||||||
|
elif val in meme[1]:
|
||||||
|
containing.append(title_case(meme[1]))
|
||||||
|
|
||||||
|
return [*starting, *containing]
|
||||||
|
|
||||||
|
|
||||||
class FunSlash(commands.Cog):
|
class FunSlash(commands.Cog):
|
||||||
def __init__(self, client: Didier):
|
def __init__(self, client: Didier):
|
||||||
self.client: Didier = client
|
self.client: Didier = client
|
||||||
|
|
||||||
@slash_command(
|
@slash_command(name="xkcd", description="Zoek xkcd comics")
|
||||||
name="xkcd",
|
async def _xkcd_slash(self, ctx: ApplicationContext,
|
||||||
description="Zoek xkcd comics",
|
num: Option(int, description="Nummer van de comic (default de comic van vandaag).", required=False, default=None)
|
||||||
options=[
|
):
|
||||||
Option(
|
return await ctx.respond(embed=XKCDEmbed(num).create())
|
||||||
"num",
|
|
||||||
description="Nummer van de comic (default de comic van vandaag).",
|
@slash_command(name="memes", description="Lijst van memegen-memes")
|
||||||
type=OptionType.INTEGER,
|
async def _memes_slash(self, ctx: ApplicationContext):
|
||||||
required=False
|
return await MemesList(ctx=ctx).respond()
|
||||||
)
|
|
||||||
]
|
@slash_command(name="memegen", description="Genereer memes")
|
||||||
)
|
async def _memegen_slash(self, ctx: ApplicationContext,
|
||||||
async def _xkcd_slash(self, interaction: SlashInteraction, num: int = None):
|
meme: Option(str, description="Naam van de template", required=True, autocomplete=autocomplete_memes),
|
||||||
return await interaction.reply(embed=XKCDEmbed(num).create())
|
field1: Option(str, required=True),
|
||||||
|
field2: Option(str, required=False, default=""),
|
||||||
|
field3: Option(str, required=False, default=""),
|
||||||
|
field4: Option(str, required=False, default="")):
|
||||||
|
# Get the meme info that corresponds to this name
|
||||||
|
result: memes.Meme = memes.getMeme(meme)
|
||||||
|
|
||||||
|
# No meme found
|
||||||
|
if result is None:
|
||||||
|
return await ctx.respond("Deze meme staat niet in de database.", ephemeral=True)
|
||||||
|
|
||||||
|
await ctx.response.defer()
|
||||||
|
|
||||||
|
fields = (field1, field2, field3, field4)
|
||||||
|
generated = generate(result, fields)
|
||||||
|
|
||||||
|
# Send generated meme or error message
|
||||||
|
await ctx.send_followup(generated["message"])
|
||||||
|
|
||||||
|
|
||||||
def setup(client: Didier):
|
def setup(client: Didier):
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from dislash import slash_command, SlashInteraction, Option, OptionType
|
from discord.commands import slash_command, ApplicationContext, Option
|
||||||
from functions.scrapers.google import google_search, create_google_embed
|
from functions.scrapers.google import google_search, create_google_embed
|
||||||
from startup.didier import Didier
|
from startup.didier import Didier
|
||||||
|
|
||||||
|
@ -8,20 +8,15 @@ class GoogleSlash(commands.Cog):
|
||||||
def __init__(self, client: Didier):
|
def __init__(self, client: Didier):
|
||||||
self.client: Didier = client
|
self.client: Didier = client
|
||||||
|
|
||||||
@slash_command(name="google",
|
@slash_command(name="google", description="Google search")
|
||||||
description="Google search",
|
async def _google_slash(self, ctx: ApplicationContext, query: Option(str, "Search query")):
|
||||||
options=[
|
|
||||||
Option("query", "Search query", OptionType.STRING, required=True)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
async def _google_slash(self, interaction: SlashInteraction, query: str):
|
|
||||||
result = google_search(query)
|
result = google_search(query)
|
||||||
|
|
||||||
if not result.results:
|
if not result.results:
|
||||||
return await interaction.reply("Er ging iets fout (Response {})".format(result.status_code))
|
return await ctx.respond("Er ging iets fout (Response {})".format(result.status_code))
|
||||||
|
|
||||||
embed = create_google_embed(result)
|
embed = create_google_embed(result)
|
||||||
await interaction.reply(embed=embed)
|
await ctx.respond(embed=embed)
|
||||||
|
|
||||||
|
|
||||||
def setup(client: Didier):
|
def setup(client: Didier):
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
from discord.ext import commands
|
||||||
|
from discord.commands import slash_command, ApplicationContext, AutocompleteContext, Option
|
||||||
|
from requests import get
|
||||||
|
|
||||||
|
from data.links import load_all_links, get_link_for
|
||||||
|
from startup.didier import Didier
|
||||||
|
|
||||||
|
links = load_all_links()
|
||||||
|
|
||||||
|
|
||||||
|
def link_autocomplete(ctx: AutocompleteContext) -> list[str]:
|
||||||
|
return [link for link in links if link.lower().startswith(ctx.value.lower())]
|
||||||
|
|
||||||
|
|
||||||
|
class OtherSlash(commands.Cog):
|
||||||
|
def __init__(self, client: Didier):
|
||||||
|
self.client: Didier = client
|
||||||
|
|
||||||
|
@slash_command(name="inspire", description="Genereer quotes via Inspirobot.")
|
||||||
|
async def _inspire_slash(self, ctx: ApplicationContext):
|
||||||
|
image = get("https://inspirobot.me/api?generate=true")
|
||||||
|
|
||||||
|
if image.status_code == 200:
|
||||||
|
await ctx.respond(image.text)
|
||||||
|
else:
|
||||||
|
await ctx.respond("Uh oh API down.")
|
||||||
|
|
||||||
|
@slash_command(name="link", description="Shortcut voor nuttige links.")
|
||||||
|
async def _link_slash(self, ctx: ApplicationContext,
|
||||||
|
name: Option(str, description="Naam van de link.", required=True,
|
||||||
|
autocomplete=link_autocomplete)):
|
||||||
|
match = get_link_for(name)
|
||||||
|
|
||||||
|
if match is None:
|
||||||
|
return await ctx.respond(f"Geen match gevonden voor \"{name}\".")
|
||||||
|
|
||||||
|
return await ctx.respond(match)
|
||||||
|
|
||||||
|
|
||||||
|
def setup(client: Didier):
|
||||||
|
client.add_cog(OtherSlash(client))
|
|
@ -1,7 +1,8 @@
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from dislash import SlashInteraction, slash_command, Option, OptionType
|
from discord.commands import slash_command, ApplicationContext, Option, AutocompleteContext
|
||||||
|
|
||||||
from data import schedule
|
from data import schedule
|
||||||
|
from data.courses import load_courses, find_course_from_name
|
||||||
from data.embeds.food import Menu
|
from data.embeds.food import Menu
|
||||||
from data.embeds.deadlines import Deadlines
|
from data.embeds.deadlines import Deadlines
|
||||||
from functions import les, config
|
from functions import les, config
|
||||||
|
@ -10,66 +11,77 @@ from functions.timeFormatters import skip_weekends
|
||||||
from startup.didier import Didier
|
from startup.didier import Didier
|
||||||
|
|
||||||
|
|
||||||
|
# Preload autocomplete constants to allow for smoother results
|
||||||
|
courses = load_courses()
|
||||||
|
days = ["Morgen", "Overmorgen", "Maandag", "Dinsdag", "Woensdag", "Donderdag", "Vrijdag"]
|
||||||
|
|
||||||
|
|
||||||
|
def day_autocomplete(ctx: AutocompleteContext) -> list[str]:
|
||||||
|
return [day for day in days if day.lower().startswith(ctx.value.lower())]
|
||||||
|
|
||||||
|
|
||||||
|
def course_autocomplete(ctx: AutocompleteContext) -> list[str]:
|
||||||
|
return [course for course in courses if course.lower().startswith(ctx.value.lower())]
|
||||||
|
|
||||||
|
|
||||||
class SchoolSlash(commands.Cog):
|
class SchoolSlash(commands.Cog):
|
||||||
def __init__(self, client: Didier):
|
def __init__(self, client: Didier):
|
||||||
self.client: Didier = client
|
self.client: Didier = client
|
||||||
|
|
||||||
@slash_command(
|
@slash_command(name="eten", description="Menu in de UGent resto's op een bepaalde dag")
|
||||||
name="eten",
|
async def _food_slash(self, ctx: ApplicationContext,
|
||||||
description="Menu in de UGENT resto's op een bepaalde dag",
|
dag: Option(str, description="Dag", required=False, default=None, autocomplete=day_autocomplete)
|
||||||
options=[
|
):
|
||||||
Option(
|
|
||||||
"dag",
|
|
||||||
description="Dag",
|
|
||||||
type=OptionType.STRING
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
async def _food_slash(self, interaction: SlashInteraction, dag: str = None):
|
|
||||||
embed = Menu(dag).to_embed()
|
embed = Menu(dag).to_embed()
|
||||||
await interaction.reply(embed=embed)
|
await ctx.respond(embed=embed)
|
||||||
|
|
||||||
@slash_command(name="deadlines", description="Aanstaande deadlines")
|
@slash_command(name="deadlines", description="Aanstaande deadlines")
|
||||||
async def _deadlines_slash(self, interaction: SlashInteraction):
|
async def _deadlines_slash(self, ctx: ApplicationContext):
|
||||||
embed = Deadlines().to_embed()
|
embed = Deadlines().to_embed()
|
||||||
await interaction.reply(embed=embed)
|
await ctx.respond(embed=embed)
|
||||||
|
|
||||||
@slash_command(
|
@slash_command(name="les", description="Lessenrooster voor [Dag] (default vandaag)",)
|
||||||
name="les",
|
async def _schedule_slash(self, ctx: ApplicationContext,
|
||||||
description="Lessenrooster voor [Dag] (default vandaag)",
|
dag: Option(str, description="Dag", required=False, default=None, autocomplete=day_autocomplete)
|
||||||
options=[
|
):
|
||||||
Option(
|
|
||||||
"dag",
|
|
||||||
description="dag",
|
|
||||||
type=OptionType.STRING,
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
async def _schedule_slash(self, interaction: SlashInteraction, day: str = None):
|
|
||||||
"""It's late and I really don't want to refactor the original right now"""
|
"""It's late and I really don't want to refactor the original right now"""
|
||||||
if day is not None:
|
if dag is not None:
|
||||||
day = day.lower()
|
dag = dag.lower()
|
||||||
|
|
||||||
date = les.find_target_date(day)
|
date = les.find_target_date(dag)
|
||||||
|
|
||||||
# Person explicitly requested a weekend-day
|
# Person explicitly requested a weekend-day
|
||||||
if day is not None and day.lower() in ("morgen", "overmorgen") and date.weekday() > 4:
|
if dag is not None and dag.lower() in ("morgen", "overmorgen") and date.weekday() > 4:
|
||||||
return await interaction.reply(f"{capitalize(day)} is het weekend.", ephemeral=True)
|
return await ctx.respond(f"{capitalize(dag)} is het weekend.", ephemeral=True)
|
||||||
|
|
||||||
date = skip_weekends(date)
|
date = skip_weekends(date)
|
||||||
|
|
||||||
s = schedule.Schedule(date, int(config.get("year")), int(config.get("semester")), day is not None)
|
s = schedule.Schedule(date, int(config.get("year")), int(config.get("semester")), dag is not None)
|
||||||
|
|
||||||
if s.semester_over:
|
if s.semester_over:
|
||||||
return await interaction.reply("Het semester is afgelopen.", ephemeral=True)
|
return await ctx.respond("Het semester is afgelopen.", ephemeral=True)
|
||||||
|
|
||||||
# DM only shows user's own minor
|
# DM only shows user's own minor
|
||||||
if interaction.guild is None:
|
if ctx.guild is None:
|
||||||
minor_roles = [*schedule.find_minor(self.client, interaction.author.id)]
|
minor_roles = [*schedule.find_minor(self.client, ctx.interaction.user.id)]
|
||||||
return await interaction.reply(embed=s.create_schedule(minor_roles=minor_roles).to_embed())
|
return await ctx.respond(embed=s.create_schedule(minor_roles=minor_roles).to_embed())
|
||||||
|
|
||||||
return await interaction.reply(embed=s.create_schedule().to_embed())
|
return await ctx.respond(embed=s.create_schedule().to_embed())
|
||||||
|
|
||||||
|
@slash_command(name="fiche", description="Zoek de studiefiche voor een vak.")
|
||||||
|
async def _study_guide_slash(self, ctx: ApplicationContext,
|
||||||
|
vak: Option(str, description="Naam van het vak. Afkortingen werken ook, maar worden niet geautocompletet.",
|
||||||
|
required=True, autocomplete=course_autocomplete)):
|
||||||
|
# Find code corresponding to the search query
|
||||||
|
course = find_course_from_name(vak, courses)
|
||||||
|
|
||||||
|
# Code not found
|
||||||
|
if course is None:
|
||||||
|
return await ctx.respond(f"Onbekend vak: \"{vak}\".", ephemeral=True)
|
||||||
|
|
||||||
|
# Get the guide for the current year
|
||||||
|
year = 2018 + int(config.get("year"))
|
||||||
|
return await ctx.respond(f"https://studiekiezer.ugent.be/studiefiche/nl/{course.code}/{year}")
|
||||||
|
|
||||||
|
|
||||||
def setup(client: Didier):
|
def setup(client: Didier):
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from dislash import SlashInteraction, slash_command, Option, OptionType
|
from discord.commands import slash_command, ApplicationContext, Option
|
||||||
|
|
||||||
from data.embeds.translate import Translation
|
from data.embeds.translate import Translation
|
||||||
from startup.didier import Didier
|
from startup.didier import Didier
|
||||||
|
@ -9,18 +9,14 @@ class TranslateSlash(commands.Cog):
|
||||||
def __init__(self, client: Didier):
|
def __init__(self, client: Didier):
|
||||||
self.client: Didier = client
|
self.client: Didier = client
|
||||||
|
|
||||||
@slash_command(
|
@slash_command(name="translate", description="Google Translate")
|
||||||
name="translate",
|
async def _translate_slash(self, ctx: ApplicationContext,
|
||||||
description="Google Translate",
|
text: Option(str, description="Tekst om te vertalen"),
|
||||||
options=[
|
from_lang: Option(str, description="Taal om van te vertalen (default auto-detect)", default="auto"),
|
||||||
Option("text", "Tekst om te vertalen", OptionType.STRING, required=True),
|
to_lang: Option(str, description="Taal om naar te vertalen (default NL)", default="nl")
|
||||||
Option("from_lang", "Taal om van te vertalen (default auto-detect)", OptionType.STRING),
|
):
|
||||||
Option("to_lang", "Taal om naar te vertalen (default NL)", OptionType.STRING)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
async def _translate_slash(self, interaction: SlashInteraction, text: str, from_lang: str = "auto", to_lang: str = "nl"):
|
|
||||||
translation = Translation(text=text, fr=from_lang.lower(), to=to_lang.lower())
|
translation = Translation(text=text, fr=from_lang.lower(), to=to_lang.lower())
|
||||||
await interaction.reply(embed=translation.to_embed())
|
await ctx.respond(embed=translation.to_embed())
|
||||||
|
|
||||||
|
|
||||||
def setup(client: Didier):
|
def setup(client: Didier):
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
from converters.numbers import Abbreviated
|
from converters.numbers import Abbreviated
|
||||||
from data.menus import storePages
|
|
||||||
from decorators import help
|
from decorators import help
|
||||||
import discord
|
import discord
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
|
@ -22,11 +21,12 @@ class Store(commands.Cog):
|
||||||
@commands.check(checks.allowedChannels)
|
@commands.check(checks.allowedChannels)
|
||||||
@help.Category(Category.Currency)
|
@help.Category(Category.Currency)
|
||||||
async def store(self, ctx):
|
async def store(self, ctx):
|
||||||
entries = store.getAllItems()
|
pass
|
||||||
await storePages.Pages(source=storePages.Source(entries), clear_reactions_after=True).start(ctx)
|
# entries = store.getAllItems()
|
||||||
|
# await storePages.Pages(source=storePages.Source(entries), clear_reactions_after=True).start(ctx)
|
||||||
|
|
||||||
@store.command(name="Buy", aliases=["Get"], hidden=True)
|
@store.command(name="Buy", aliases=["Get"], hidden=True)
|
||||||
async def storeBuy(self, ctx, item, amount: Abbreviated = 1):
|
async def store_buy(self, ctx, item, amount: Abbreviated = 1):
|
||||||
if amount is None:
|
if amount is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ class Store(commands.Cog):
|
||||||
))
|
))
|
||||||
|
|
||||||
@store.command(name="Sell", hidden=True)
|
@store.command(name="Sell", hidden=True)
|
||||||
async def storeSell(self, ctx, itemid, amount: Abbreviated = 1):
|
async def store_sell(self, ctx, itemid, amount: Abbreviated = 1):
|
||||||
if amount is None:
|
if amount is None:
|
||||||
return
|
return
|
||||||
await self.sell(ctx, itemid, amount)
|
await self.sell(ctx, itemid, amount)
|
||||||
|
|
127
cogs/train.py
127
cogs/train.py
|
@ -1,127 +0,0 @@
|
||||||
from data.menus import paginatedLeaderboard
|
|
||||||
from decorators import help
|
|
||||||
import discord
|
|
||||||
from discord.ext import commands, menus
|
|
||||||
from enums.help_categories import Category
|
|
||||||
from functions import checks, timeFormatters
|
|
||||||
import requests
|
|
||||||
|
|
||||||
|
|
||||||
class Train(commands.Cog):
|
|
||||||
|
|
||||||
def __init__(self, client):
|
|
||||||
self.client = client
|
|
||||||
|
|
||||||
# Don't allow any commands to work when locked
|
|
||||||
def cog_check(self, ctx):
|
|
||||||
return not self.client.locked
|
|
||||||
|
|
||||||
@commands.command(name="Train", aliases=["Trein"], usage="[Vertrek]* [Bestemming]")
|
|
||||||
@help.Category(category=Category.School)
|
|
||||||
async def train(self, ctx, *args):
|
|
||||||
if not args or len(args) > 2:
|
|
||||||
await ctx.send("Controleer je argumenten.")
|
|
||||||
return
|
|
||||||
destination = args[-1]
|
|
||||||
departure = args[0] if len(args) > 1 else "Gent Sint-Pieters"
|
|
||||||
|
|
||||||
req = requests.get(
|
|
||||||
"http://api.irail.be/connections/?from={}&to={}&alerts=true&lang=nl&format=json".format(departure,
|
|
||||||
destination)).json()
|
|
||||||
if "error" in req:
|
|
||||||
embed = discord.Embed(colour=discord.Colour.red())
|
|
||||||
embed.set_author(name="Treinen van {} naar {}".format(
|
|
||||||
self.formatCity(departure), self.formatCity(destination)))
|
|
||||||
embed.add_field(name="Error", value="Er ging iets fout, probeer het later opnieuw.", inline=False)
|
|
||||||
await self.sendEmbed(ctx, embed)
|
|
||||||
return
|
|
||||||
|
|
||||||
pages = paginatedLeaderboard.Pages(source=TrainPagination(self.formatConnections(req["connection"]),
|
|
||||||
self.formatCity(departure),
|
|
||||||
self.formatCity(destination)),
|
|
||||||
clear_reactions_after=True)
|
|
||||||
await pages.start(ctx)
|
|
||||||
|
|
||||||
def formatConnections(self, connections):
|
|
||||||
response = []
|
|
||||||
for connection in sorted(connections, key=lambda con: con["departure"]["time"]):
|
|
||||||
conn = {}
|
|
||||||
if connection["departure"]["canceled"] != "0" or connection["arrival"]["canceled"] != "0":
|
|
||||||
conn = {"Canceled": "Afgeschaft"}
|
|
||||||
dep = connection["departure"]
|
|
||||||
arr = connection["arrival"]
|
|
||||||
conn["depStation"] = self.formatCity(dep["station"])
|
|
||||||
conn["depTime"] = self.formatTime(dep["time"])
|
|
||||||
conn["delay"] = self.formatDelay(dep["delay"])
|
|
||||||
conn["track"] = dep["platform"]
|
|
||||||
conn["arrStation"] = self.formatCity(arr["station"])
|
|
||||||
conn["direction"] = self.formatCity(dep["direction"]["name"])
|
|
||||||
conn["arrTime"] = self.formatTime(arr["time"])
|
|
||||||
conn["duration"] = self.formatTime(connection["duration"])
|
|
||||||
response.append(conn)
|
|
||||||
return response
|
|
||||||
|
|
||||||
def formatTime(self, timestamp):
|
|
||||||
if int(timestamp) <= 86400:
|
|
||||||
minutes = int(timestamp) // 60
|
|
||||||
if minutes < 60:
|
|
||||||
return str(minutes) + "m"
|
|
||||||
return "{}h{:02}m".format(minutes // 60, minutes % 60)
|
|
||||||
else:
|
|
||||||
return timeFormatters.epochToDate(int(timestamp), "%H:%M")["date"]
|
|
||||||
|
|
||||||
def formatDelay(self, seconds):
|
|
||||||
seconds = int(seconds)
|
|
||||||
return self.sign(seconds) + self.formatTime(abs(seconds)) if seconds != 0 else ""
|
|
||||||
|
|
||||||
def sign(self, number):
|
|
||||||
return "-" if int(number) < 0 else "+"
|
|
||||||
|
|
||||||
def formatCity(self, city):
|
|
||||||
city = city[0].upper() + city[1:]
|
|
||||||
arr = []
|
|
||||||
for i, letter in enumerate(city):
|
|
||||||
if (i > 0 and (city[i - 1] == " " or city[i - 1] == "-")) or i == 0:
|
|
||||||
arr.append(letter.upper())
|
|
||||||
else:
|
|
||||||
arr.append(letter.lower())
|
|
||||||
return "".join(arr)
|
|
||||||
|
|
||||||
async def sendEmbed(self, ctx, embed):
|
|
||||||
if await checks.allowedChannels(ctx):
|
|
||||||
await ctx.send(embed=embed)
|
|
||||||
else:
|
|
||||||
await ctx.author.send(embed=embed)
|
|
||||||
|
|
||||||
|
|
||||||
class TrainPagination(menus.ListPageSource):
|
|
||||||
def __init__(self, data, departure, destination):
|
|
||||||
super().__init__(data, per_page=3)
|
|
||||||
self.departure = departure
|
|
||||||
self.destination = destination
|
|
||||||
|
|
||||||
async def format_page(self, menu: menus.MenuPages, entries):
|
|
||||||
offset = menu.current_page * self.per_page
|
|
||||||
embed = discord.Embed(colour=discord.Colour.blue())
|
|
||||||
embed.set_author(name="Treinen van {} naar {}".format(self.departure, self.destination))
|
|
||||||
embed.set_footer(text="{}/{}".format(menu.current_page + 1, self.get_max_pages()))
|
|
||||||
|
|
||||||
for i, connection in enumerate(entries, start=offset):
|
|
||||||
afgeschaft = "Canceled" in connection
|
|
||||||
embed.add_field(name="Van", value=str(connection["depStation"]), inline=True)
|
|
||||||
embed.add_field(name="Om", value=str(connection["depTime"]), inline=True)
|
|
||||||
embed.add_field(name="Spoor", value=str(connection["track"]), inline=True)
|
|
||||||
embed.add_field(name="Richting", value=str(connection["direction"]), inline=True)
|
|
||||||
embed.add_field(name="Aankomst", value=(str(connection["arrTime"])
|
|
||||||
if not afgeschaft else "**AFGESCHAFT**"), inline=True)
|
|
||||||
embed.add_field(name="Vertraging", value=str(connection["delay"]) if connection["delay"] != "" else "0",
|
|
||||||
inline=True)
|
|
||||||
|
|
||||||
# White space
|
|
||||||
if i - offset < 2:
|
|
||||||
embed.add_field(name="\u200b", value="\u200b", inline=False)
|
|
||||||
return embed
|
|
||||||
|
|
||||||
|
|
||||||
def setup(client):
|
|
||||||
client.add_cog(Train(client))
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import dacite
|
||||||
|
import json
|
||||||
|
from os import path
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Course:
|
||||||
|
abbreviations: list[str]
|
||||||
|
code: str
|
||||||
|
name: str
|
||||||
|
year: int
|
||||||
|
alt: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
def load_courses() -> dict[str, Course]:
|
||||||
|
"""Create a list of all courses"""
|
||||||
|
# Allows testing
|
||||||
|
filepath = path.join(path.dirname(__file__), "..", "files", "courses.json")
|
||||||
|
with open(filepath, "r") as file:
|
||||||
|
data = json.load(file)
|
||||||
|
|
||||||
|
courses = {}
|
||||||
|
|
||||||
|
for course_name in data:
|
||||||
|
# Add name into the dict to allow flexibility
|
||||||
|
course_data = data[course_name]
|
||||||
|
course_data["name"] = course_name
|
||||||
|
|
||||||
|
courses[course_name] = dacite.from_dict(data_class=Course, data=course_data)
|
||||||
|
|
||||||
|
return courses
|
||||||
|
|
||||||
|
|
||||||
|
def find_course_from_name(name: str, courses: Optional[dict[str, Course]] = None, case_insensitive: bool = True) -> Optional[Course]:
|
||||||
|
# Allow passing a course dict in to avoid having to create it all the time
|
||||||
|
if courses is None:
|
||||||
|
courses = load_courses()
|
||||||
|
|
||||||
|
if case_insensitive:
|
||||||
|
name = name.lower()
|
||||||
|
|
||||||
|
def _perhaps_lower(inp: str) -> str:
|
||||||
|
"""Cast a string to lowercase if necessary"""
|
||||||
|
if case_insensitive:
|
||||||
|
return inp.lower()
|
||||||
|
|
||||||
|
return inp
|
||||||
|
|
||||||
|
# Iterate over all courses to look for a match
|
||||||
|
for course_name, course in courses.items():
|
||||||
|
# Check name first
|
||||||
|
if _perhaps_lower(course_name) == name:
|
||||||
|
return course
|
||||||
|
|
||||||
|
# Then abbreviations
|
||||||
|
for abbreviation in course.abbreviations:
|
||||||
|
if _perhaps_lower(abbreviation) == name:
|
||||||
|
return course
|
||||||
|
|
||||||
|
# Finally alternative names
|
||||||
|
if course.alt is not None and _perhaps_lower(course.alt) == name:
|
||||||
|
return course
|
||||||
|
|
||||||
|
return None
|
|
@ -0,0 +1,16 @@
|
||||||
|
import json
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
|
def load_all_links() -> dict[str, str]:
|
||||||
|
with open("files/links.json", "r") as file:
|
||||||
|
return json.load(file)
|
||||||
|
|
||||||
|
|
||||||
|
def get_link_for(name: str) -> Optional[str]:
|
||||||
|
links = load_all_links()
|
||||||
|
for link in links:
|
||||||
|
if link.lower() == name.lower():
|
||||||
|
return links[link]
|
||||||
|
|
||||||
|
return None
|
|
@ -1,21 +1,18 @@
|
||||||
import discord
|
from typing import Union
|
||||||
from discord.ext import menus
|
|
||||||
|
from discord import ApplicationContext
|
||||||
|
from discord.ext.commands import Context
|
||||||
|
|
||||||
|
from data.menus.paginated import Paginated
|
||||||
|
from functions.database.custom_commands import get_all
|
||||||
|
from functions.stringFormatters import capitalize
|
||||||
|
|
||||||
|
|
||||||
class CommandsList(menus.ListPageSource):
|
class CommandsList(Paginated):
|
||||||
def __init__(self, data, colour=discord.Colour.blue()):
|
def __init__(self, ctx: Union[ApplicationContext, Context]):
|
||||||
super().__init__(data, per_page=15)
|
all_commands = get_all()
|
||||||
self.colour = colour
|
commands_sorted = list(sorted(map(lambda x: (capitalize(x["name"]),), all_commands)))
|
||||||
|
super().__init__(ctx=ctx, title="Custom Commands", data=commands_sorted, per_page=15)
|
||||||
|
|
||||||
async def format_page(self, menu: menus.MenuPages, entries):
|
def format_entry(self, index: int, value: tuple) -> str:
|
||||||
embed = discord.Embed(colour=self.colour)
|
return value[0]
|
||||||
embed.set_author(name="Custom Commands")
|
|
||||||
embed.description = "\n".join(entries)
|
|
||||||
embed.set_footer(text="{}/{}".format(menu.current_page + 1, self.get_max_pages()))
|
|
||||||
|
|
||||||
return embed
|
|
||||||
|
|
||||||
|
|
||||||
class Pages(menus.MenuPages):
|
|
||||||
def __init__(self, source, clear_reactions_after, timeout=30.0):
|
|
||||||
super().__init__(source, timeout=timeout, delete_message_after=True, clear_reactions_after=clear_reactions_after)
|
|
||||||
|
|
|
@ -0,0 +1,246 @@
|
||||||
|
import math
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Union, Optional
|
||||||
|
|
||||||
|
import discord
|
||||||
|
import requests
|
||||||
|
from discord import ApplicationContext
|
||||||
|
from discord.ext.commands import Context
|
||||||
|
|
||||||
|
from data.menus.paginated import Paginated
|
||||||
|
from enums.numbers import Numbers
|
||||||
|
from functions import xp
|
||||||
|
from functions.database import currency, stats, poke, muttn
|
||||||
|
from functions.utils import get_display_name
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Leaderboard(Paginated, ABC):
|
||||||
|
highlight: str = None
|
||||||
|
colour: discord.Colour = discord.Colour.blue()
|
||||||
|
fetch_names: bool = True
|
||||||
|
ignore_non_pos: bool = True
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
self.data = self.process_data(self.get_data())
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_data(self) -> list[tuple]:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def process_data(self, entries: list[tuple]) -> Optional[list[tuple]]:
|
||||||
|
data = []
|
||||||
|
for i, v in enumerate(sorted(entries, key=self.get_value, reverse=True)):
|
||||||
|
entry_data = self.get_value(v)
|
||||||
|
|
||||||
|
# Leaderboard is empty
|
||||||
|
if i == 0 and entry_data == 0 and self.ignore_non_pos:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Ignore entries with no data
|
||||||
|
if self.ignore_non_pos and entry_data <= 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
data.append((self.get_key(v), f"{entry_data:,}", entry_data,))
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def get_key(self, data: tuple):
|
||||||
|
return data[0]
|
||||||
|
|
||||||
|
def get_value(self, data: tuple):
|
||||||
|
return data[1]
|
||||||
|
|
||||||
|
def _should_highlight(self, data) -> bool:
|
||||||
|
"""Check if an entry should be highlighted"""
|
||||||
|
if self.fetch_names:
|
||||||
|
return data == self.ctx.author.id
|
||||||
|
|
||||||
|
return data == self.highlight
|
||||||
|
|
||||||
|
def format_entry_data(self, data: tuple) -> str:
|
||||||
|
return str(data[1])
|
||||||
|
|
||||||
|
def format_entry(self, index: int, data: tuple) -> str:
|
||||||
|
name = data[0]
|
||||||
|
|
||||||
|
if self.fetch_names:
|
||||||
|
name = get_display_name(self.ctx, int(data[0]))
|
||||||
|
|
||||||
|
s = f"{index + 1}: {name} ({self.format_entry_data(data)})"
|
||||||
|
|
||||||
|
if self._should_highlight(data[0]):
|
||||||
|
return f"**{s}**"
|
||||||
|
|
||||||
|
return s
|
||||||
|
|
||||||
|
@property
|
||||||
|
def empty_description(self) -> str:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
async def empty_leaderboard(self, ctx: Union[ApplicationContext, Context], **kwargs):
|
||||||
|
embed = discord.Embed(colour=self.colour)
|
||||||
|
embed.set_author(name=self.title)
|
||||||
|
embed.description = self.empty_description
|
||||||
|
|
||||||
|
if isinstance(ctx, ApplicationContext):
|
||||||
|
return await ctx.respond(embed=embed)
|
||||||
|
|
||||||
|
return await ctx.reply(embed=embed, **kwargs)
|
||||||
|
|
||||||
|
async def respond(self, **kwargs) -> discord.Message:
|
||||||
|
if self.data is None:
|
||||||
|
return await self.empty_leaderboard(self.ctx, **kwargs)
|
||||||
|
|
||||||
|
return await super().respond(**kwargs)
|
||||||
|
|
||||||
|
async def send(self, **kwargs) -> discord.Message:
|
||||||
|
if self.data is None:
|
||||||
|
return await self.empty_leaderboard(self.ctx, mention_author=False, **kwargs)
|
||||||
|
|
||||||
|
return await super().send(mention_author=False, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class BitcoinLeaderboard(Leaderboard):
|
||||||
|
title: str = field(default="Bitcoin Leaderboard")
|
||||||
|
|
||||||
|
def get_data(self) -> list[tuple]:
|
||||||
|
return currency.getAllRows()
|
||||||
|
|
||||||
|
def get_value(self, data: tuple):
|
||||||
|
return round(float(data[8]), 8)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def empty_description(self) -> str:
|
||||||
|
return "Er zijn nog geen personen met Bitcoins."
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class CoronaLeaderboard(Leaderboard):
|
||||||
|
colour: discord.Colour = field(default=discord.Colour.red())
|
||||||
|
fetch_names: bool = field(default=False)
|
||||||
|
highlight: str = field(default="Belgium")
|
||||||
|
title: str = field(default="Corona Leaderboard")
|
||||||
|
|
||||||
|
def get_data(self) -> list[tuple]:
|
||||||
|
result = requests.get("https://disease.sh/v3/covid-19/countries").json()
|
||||||
|
result.sort(key=lambda x: int(x["cases"]), reverse=True)
|
||||||
|
|
||||||
|
data = []
|
||||||
|
for country in result:
|
||||||
|
data.append((country["country"], f"{country['cases']:,}", country["cases"]))
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def get_value(self, data: tuple):
|
||||||
|
return data[2]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class DinksLeaderboard(Leaderboard):
|
||||||
|
title: str = field(default="Dinks Leaderboard")
|
||||||
|
|
||||||
|
def get_data(self) -> list[tuple]:
|
||||||
|
entries = currency.getAllRows()
|
||||||
|
platDinks = currency.getAllPlatDinks()
|
||||||
|
|
||||||
|
# Take platinum dinks into account
|
||||||
|
for i, user in enumerate(entries):
|
||||||
|
if str(user[0]) in platDinks:
|
||||||
|
# Tuples don't support assignment, cast to list
|
||||||
|
user = list(user)
|
||||||
|
user[1] += platDinks[str(user[0])] * Numbers.q.value
|
||||||
|
entries[i] = user
|
||||||
|
|
||||||
|
return entries
|
||||||
|
|
||||||
|
def get_value(self, data: tuple):
|
||||||
|
return float(data[1]) + float(data[3])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def empty_description(self) -> str:
|
||||||
|
return "Er zijn nog geen personen met Didier Dinks."
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MessageLeaderboard(Leaderboard):
|
||||||
|
title: str = field(default="Message Leaderboard")
|
||||||
|
message_count: int = field(init=False)
|
||||||
|
|
||||||
|
def get_data(self) -> list[tuple]:
|
||||||
|
entries = stats.getAllRows()
|
||||||
|
self.message_count = stats.getTotalMessageCount()
|
||||||
|
return entries
|
||||||
|
|
||||||
|
def get_value(self, data: tuple):
|
||||||
|
return round(int(data[11]))
|
||||||
|
|
||||||
|
def format_entry_data(self, data: tuple) -> str:
|
||||||
|
perc = round(data[2] * 100 / self.message_count, 2)
|
||||||
|
return f"{data[2]:,} | {perc}%"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MuttnLeaderboard(Leaderboard):
|
||||||
|
title: str = field(default="Muttn Leaderboard")
|
||||||
|
|
||||||
|
def get_data(self) -> list[tuple]:
|
||||||
|
return muttn.getAllRows()
|
||||||
|
|
||||||
|
def get_value(self, data: tuple):
|
||||||
|
return round(float(data[1]), 2)
|
||||||
|
|
||||||
|
def format_entry_data(self, data: tuple) -> str:
|
||||||
|
return f"{data[2]}%"
|
||||||
|
|
||||||
|
def empty_description(self) -> str:
|
||||||
|
return "Der zittn nog geen muttns in de server."
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PokeLeaderboard(Leaderboard):
|
||||||
|
title: str = field(default="Poke Leaderboard")
|
||||||
|
|
||||||
|
def get_data(self) -> list[tuple]:
|
||||||
|
data = stats.getAllRows()
|
||||||
|
blacklist = poke.getAllBlacklistedUsers()
|
||||||
|
return list(filter(lambda x: x[0] not in blacklist, data))
|
||||||
|
|
||||||
|
def get_value(self, data: tuple):
|
||||||
|
return round(int(data[1]))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def empty_description(self) -> str:
|
||||||
|
return "Er is nog niemand getikt."
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class RobLeaderboard(Leaderboard):
|
||||||
|
title: str = field(default="Rob Leaderboard")
|
||||||
|
|
||||||
|
def get_data(self) -> list[tuple]:
|
||||||
|
return list(stats.getAllRows())
|
||||||
|
|
||||||
|
def get_value(self, data: tuple):
|
||||||
|
return math.floor(float(data[4]))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def empty_description(self) -> str:
|
||||||
|
return "Er heeft nog niemand Didier Dinks gestolen."
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class XPLeaderboard(Leaderboard):
|
||||||
|
title: str = field(default="XP Leaderboard")
|
||||||
|
|
||||||
|
def get_data(self) -> list[tuple]:
|
||||||
|
return stats.getAllRows()
|
||||||
|
|
||||||
|
def get_value(self, data: tuple):
|
||||||
|
return round(int(data[12]))
|
||||||
|
|
||||||
|
def format_entry_data(self, data: tuple) -> str:
|
||||||
|
entry = data[2]
|
||||||
|
return f"Level {xp.calculate_level(entry):,} | {entry:,} XP"
|
|
@ -0,0 +1,26 @@
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
|
from data.menus.paginated import Paginated
|
||||||
|
from functions import stringFormatters
|
||||||
|
from functions.database import memes
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MemesList(Paginated):
|
||||||
|
title: str = field(default="Memes")
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
self.data = self.get_data()
|
||||||
|
|
||||||
|
def get_data(self) -> list[tuple]:
|
||||||
|
data = []
|
||||||
|
meme_list = memes.getAllMemes()
|
||||||
|
for meme in sorted(meme_list, key=lambda x: x[1]):
|
||||||
|
name = stringFormatters.title_case(meme[1])
|
||||||
|
fields = meme[2]
|
||||||
|
data.append((name, fields,))
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def format_entry(self, index: int, value: tuple) -> str:
|
||||||
|
return f"{value[0]} ({value[1]})"
|
|
@ -0,0 +1,67 @@
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
import discord
|
||||||
|
from discord import ApplicationContext
|
||||||
|
from discord.ext import pages
|
||||||
|
from discord.ext.commands import Context
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Paginated(ABC):
|
||||||
|
"""Abstract class to support paginated menus easily"""
|
||||||
|
ctx: Union[ApplicationContext, Context]
|
||||||
|
title: str
|
||||||
|
data: list[tuple] = None
|
||||||
|
per_page: int = 10
|
||||||
|
colour: discord.Colour = discord.Colour.blue()
|
||||||
|
|
||||||
|
def create_embed(self, description: str) -> discord.Embed:
|
||||||
|
embed = discord.Embed(colour=self.colour)
|
||||||
|
embed.set_author(name=self.title)
|
||||||
|
embed.description = description
|
||||||
|
|
||||||
|
return embed
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def format_entry(self, index: int, value: tuple) -> str:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def create_pages(self, data: list[tuple]) -> list[discord.Embed]:
|
||||||
|
# Amount of entries added to this page
|
||||||
|
added = 0
|
||||||
|
page_list = []
|
||||||
|
|
||||||
|
description = ""
|
||||||
|
for i, v in enumerate(data):
|
||||||
|
s = self.format_entry(i, v)
|
||||||
|
|
||||||
|
description += s + "\n"
|
||||||
|
added += 1
|
||||||
|
|
||||||
|
# Page full, create an embed & change counters
|
||||||
|
if added == self.per_page:
|
||||||
|
embed = self.create_embed(description)
|
||||||
|
|
||||||
|
description = ""
|
||||||
|
added = 0
|
||||||
|
page_list.append(embed)
|
||||||
|
|
||||||
|
# Add final embed if necessary
|
||||||
|
if added != 0:
|
||||||
|
embed = self.create_embed(description)
|
||||||
|
page_list.append(embed)
|
||||||
|
|
||||||
|
return page_list
|
||||||
|
|
||||||
|
def create_paginator(self) -> pages.Paginator:
|
||||||
|
return pages.Paginator(pages=self.create_pages(self.data), show_disabled=False, disable_on_timeout=True, timeout=30)
|
||||||
|
|
||||||
|
async def respond(self, **kwargs) -> discord.Message:
|
||||||
|
paginator = self.create_paginator()
|
||||||
|
return await paginator.respond(self.ctx.interaction, **kwargs)
|
||||||
|
|
||||||
|
async def send(self, **kwargs) -> discord.Message:
|
||||||
|
paginator = self.create_paginator()
|
||||||
|
return await paginator.send(self.ctx, **kwargs)
|
|
@ -1,31 +0,0 @@
|
||||||
import discord
|
|
||||||
from discord.ext import menus
|
|
||||||
|
|
||||||
|
|
||||||
# https://github.com/Rapptz/discord-ext-menus
|
|
||||||
class Source(menus.ListPageSource):
|
|
||||||
def __init__(self, data, name, colour=discord.Colour.blue()):
|
|
||||||
super().__init__(data, per_page=10)
|
|
||||||
self.name = name
|
|
||||||
self.colour = colour
|
|
||||||
|
|
||||||
async def format_page(self, menu: menus.MenuPages, entries):
|
|
||||||
offset = menu.current_page * self.per_page
|
|
||||||
|
|
||||||
description = ""
|
|
||||||
for i, v in enumerate(entries, start=offset):
|
|
||||||
# Check if the person's name has to be highlighted
|
|
||||||
if v.startswith("**") and v.endswith("**"):
|
|
||||||
description += "**"
|
|
||||||
v = v[2:]
|
|
||||||
description += "{}: {}\n".format(i + 1, v)
|
|
||||||
embed = discord.Embed(colour=self.colour)
|
|
||||||
embed.set_author(name=self.name)
|
|
||||||
embed.description = description
|
|
||||||
embed.set_footer(text="{}/{}".format(menu.current_page + 1, self.get_max_pages()))
|
|
||||||
return embed
|
|
||||||
|
|
||||||
|
|
||||||
class Pages(menus.MenuPages):
|
|
||||||
def __init__(self, source, clear_reactions_after, timeout=30.0):
|
|
||||||
super().__init__(source, timeout=timeout, delete_message_after=True, clear_reactions_after=clear_reactions_after)
|
|
|
@ -1,28 +0,0 @@
|
||||||
import discord
|
|
||||||
from discord.ext import menus
|
|
||||||
|
|
||||||
|
|
||||||
# https://github.com/Rapptz/discord-ext-menus
|
|
||||||
class Source(menus.ListPageSource):
|
|
||||||
def __init__(self, data):
|
|
||||||
super().__init__(data, per_page=10)
|
|
||||||
self.name = "Didier Store"
|
|
||||||
self.colour = discord.Colour.blue()
|
|
||||||
|
|
||||||
async def format_page(self, menu: menus.MenuPages, entries):
|
|
||||||
offset = menu.current_page * self.per_page
|
|
||||||
|
|
||||||
embed = discord.Embed(colour=self.colour)
|
|
||||||
embed.set_author(name=self.name)
|
|
||||||
embed.description = "Heb je een idee voor een item? DM DJ STIJN met je idee!"
|
|
||||||
embed.set_footer(text="{}/{}".format(menu.current_page + 1, self.get_max_pages()))
|
|
||||||
|
|
||||||
for i, v in enumerate(entries, start=offset):
|
|
||||||
embed.add_field(name="#{} - {}".format(v[0], v[1]), value="{:,} Didier Dinks".format(v[2]))
|
|
||||||
|
|
||||||
return embed
|
|
||||||
|
|
||||||
|
|
||||||
class Pages(menus.MenuPages):
|
|
||||||
def __init__(self, source, clear_reactions_after, timeout=30.0):
|
|
||||||
super().__init__(source, timeout=timeout, delete_message_after=True, clear_reactions_after=clear_reactions_after)
|
|
|
@ -1,12 +1,9 @@
|
||||||
import discord
|
import discord
|
||||||
from dotenv import load_dotenv
|
|
||||||
from functions.prefixes import get_prefix
|
from functions.prefixes import get_prefix
|
||||||
from settings import STATUS_MESSAGE, TOKEN
|
from settings import STATUS_MESSAGE, TOKEN
|
||||||
from startup.didier import Didier
|
from startup.didier import Didier
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
load_dotenv(verbose=True)
|
|
||||||
|
|
||||||
# Activities
|
# Activities
|
||||||
activity = discord.Activity(type=discord.ActivityType.playing, name=STATUS_MESSAGE)
|
activity = discord.Activity(type=discord.ActivityType.playing, name=STATUS_MESSAGE)
|
||||||
status = discord.Status.online
|
status = discord.Status.online
|
||||||
|
@ -16,9 +13,4 @@ if __name__ == "__main__":
|
||||||
intents.members = True
|
intents.members = True
|
||||||
|
|
||||||
client = Didier(command_prefix=get_prefix, case_insensitive=True, intents=intents, activity=activity, status=status)
|
client = Didier(command_prefix=get_prefix, case_insensitive=True, intents=intents, activity=activity, status=status)
|
||||||
|
|
||||||
# Run IPC server if necessary
|
|
||||||
if client.ipc is not None:
|
|
||||||
client.ipc.start()
|
|
||||||
|
|
||||||
client.run(TOKEN)
|
client.run(TOKEN)
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{"semester": "1", "year": "3", "years": 3, "jpl": 161733, "jpl_day": 24}
|
{"semester": "2", "year": "3", "years": 3, "jpl": 161733, "jpl_day": 24}
|
|
@ -0,0 +1,121 @@
|
||||||
|
{
|
||||||
|
"Algoritmen en Datastructuren 2": {
|
||||||
|
"abbreviations": ["AD2"],
|
||||||
|
"code": "C003777",
|
||||||
|
"year": 2
|
||||||
|
},
|
||||||
|
"Algoritmen en Datastructuren 3": {
|
||||||
|
"abbreviations": ["AD3"],
|
||||||
|
"code": "C003782",
|
||||||
|
"year": 3
|
||||||
|
},
|
||||||
|
"Artificiële Intelligentie": {
|
||||||
|
"abbreviations": ["AI"],
|
||||||
|
"code": "C003756",
|
||||||
|
"year": 3
|
||||||
|
},
|
||||||
|
"Automaten, Berekenbaarheid en Complexiteit": {
|
||||||
|
"abbreviations": ["ABC"],
|
||||||
|
"code": "C003785",
|
||||||
|
"year": 3
|
||||||
|
},
|
||||||
|
"Besturingssystemen": {
|
||||||
|
"abbreviations": ["BS"],
|
||||||
|
"code": "E019010",
|
||||||
|
"year": 3
|
||||||
|
},
|
||||||
|
"Communicatienetwerken": {
|
||||||
|
"abbreviations": ["Comnet"],
|
||||||
|
"code": "E008620",
|
||||||
|
"year": 2
|
||||||
|
},
|
||||||
|
"Computationele Biologie": {
|
||||||
|
"abbreviations": ["Compbio"],
|
||||||
|
"code": "C003789",
|
||||||
|
"year": 3
|
||||||
|
},
|
||||||
|
"Computerarchitectuur": {
|
||||||
|
"abbreviations": ["CA", "Comparch"],
|
||||||
|
"code": "E034110",
|
||||||
|
"year": 2
|
||||||
|
},
|
||||||
|
"Functioneel Programmeren": {
|
||||||
|
"abbreviations": ["FP", "Funcprog"],
|
||||||
|
"code": "C003775",
|
||||||
|
"year": 2
|
||||||
|
},
|
||||||
|
"Informatiebeveiliging": {
|
||||||
|
"abbreviations": ["Infosec"],
|
||||||
|
"alt": "Information Security",
|
||||||
|
"code": "E019400",
|
||||||
|
"year": 3
|
||||||
|
},
|
||||||
|
"Inleiding tot Elektrotechniek": {
|
||||||
|
"abbreviations": [],
|
||||||
|
"alt": "Elektrotechniek",
|
||||||
|
"code": "C003806",
|
||||||
|
"year": 3
|
||||||
|
},
|
||||||
|
"Inleiding tot Telecommunicatie": {
|
||||||
|
"abbreviations": ["Telecom"],
|
||||||
|
"code": "C003787",
|
||||||
|
"year": 3
|
||||||
|
},
|
||||||
|
"Logisch Programmeren": {
|
||||||
|
"abbreviations": ["LP", "Logprog", "Prolog"],
|
||||||
|
"code": "C003783",
|
||||||
|
"year": 3
|
||||||
|
},
|
||||||
|
"Modelleren en Simuleren": {
|
||||||
|
"abbreviations": ["Modsim"],
|
||||||
|
"code": "C003786",
|
||||||
|
"year": 3
|
||||||
|
},
|
||||||
|
"Multimedia": {
|
||||||
|
"abbreviations": ["MM"],
|
||||||
|
"code": "C002126",
|
||||||
|
"year": 2
|
||||||
|
},
|
||||||
|
"Parallelle Computersystemen": {
|
||||||
|
"abbreviations": ["Paracomp", "Parallelle", "PCS"],
|
||||||
|
"alt": "Parallel Computer Systems",
|
||||||
|
"code": "E034140",
|
||||||
|
"year": 3
|
||||||
|
},
|
||||||
|
"Statistiek en Probabiliteit": {
|
||||||
|
"abbreviations": ["Stat","Statistiek", "Statprob"],
|
||||||
|
"code": "C003778",
|
||||||
|
"year": 2
|
||||||
|
},
|
||||||
|
"Software Engineering Lab 1": {
|
||||||
|
"abbreviations": ["SEL1"],
|
||||||
|
"code": "C003780",
|
||||||
|
"year": 2
|
||||||
|
},
|
||||||
|
"Software Engineering Lab 2": {
|
||||||
|
"abbreviations": ["SEL2"],
|
||||||
|
"code": "C003784",
|
||||||
|
"year": 3
|
||||||
|
},
|
||||||
|
"Systeemprogrammeren": {
|
||||||
|
"abbreviations": ["Sysprog"],
|
||||||
|
"code": "C003776",
|
||||||
|
"year": 2
|
||||||
|
},
|
||||||
|
"Webdevelopment": {
|
||||||
|
"abbreviations": ["Webdev"],
|
||||||
|
"code": "C003779",
|
||||||
|
"year": 2
|
||||||
|
},
|
||||||
|
"Wetenschappelijk Rekenen": {
|
||||||
|
"abbreviations": ["Wetrek"],
|
||||||
|
"code": "C001521",
|
||||||
|
"year": 2
|
||||||
|
},
|
||||||
|
"Wiskundige Modellering in de Ingenieurswetenschappen": {
|
||||||
|
"abbreviations": ["Wimo"],
|
||||||
|
"alt": "Wiskundige Modellering",
|
||||||
|
"code": "C003788",
|
||||||
|
"year": 3
|
||||||
|
}
|
||||||
|
}
|
|
@ -44,24 +44,27 @@
|
||||||
"faq": "Stuurt een lijst van alle FAQ's in [Categorie] naar jouw DM's.\nGeef geen categorie op om een lijst van categorieën te krijgen.\nIndien je een of meerdere personen tagt, wordt de lijst naar hun DM's gestuurd in plaats van de jouwe.",
|
"faq": "Stuurt een lijst van alle FAQ's in [Categorie] naar jouw DM's.\nGeef geen categorie op om een lijst van categorieën te krijgen.\nIndien je een of meerdere personen tagt, wordt de lijst naar hun DM's gestuurd in plaats van de jouwe.",
|
||||||
"faq add": "Voegt een vraag & antwoord toe aan FAQ [Categorie].\nIndien je geen vraag & antwoord opgeeft, maakt het een categorie aan.",
|
"faq add": "Voegt een vraag & antwoord toe aan FAQ [Categorie].\nIndien je geen vraag & antwoord opgeeft, maakt het een categorie aan.",
|
||||||
"faq quote": "Stuurt een specifieke entry uit de FAQ in het huidige channel in plaats van de hele lijst naar iemand's DM's.",
|
"faq quote": "Stuurt een specifieke entry uit de FAQ in het huidige channel in plaats van de hele lijst naar iemand's DM's.",
|
||||||
|
"fiche": "Stuurt de URL naar de studiefiche voor [Vak]. Veelgebruikte afkortingen van vakken werken ook.\n\nVoorbeeld: Funcprog, FP, InfoSec, SEL2, ...",
|
||||||
"github": "Stuurt de GitHub link van [Persoon] indien je iemand tagt, zoniet een lijst van GitHubs van mensen uit deze Discord.\nIndien je jouw eigen GitHub hier graag bij zou zetten, gebruik je ``GitHub Add`` of kan ke een DM sturen naar DJ STIJN.",
|
"github": "Stuurt de GitHub link van [Persoon] indien je iemand tagt, zoniet een lijst van GitHubs van mensen uit deze Discord.\nIndien je jouw eigen GitHub hier graag bij zou zetten, gebruik je ``GitHub Add`` of kan ke een DM sturen naar DJ STIJN.",
|
||||||
"github add": "Voegt jouw eigen GitHub toe aan de lijst.\nZowel ``github.com/username`` als ``username`` werken.",
|
"github add": "Voegt jouw eigen GitHub toe aan de lijst.\nZowel ``github.com/username`` als ``username`` werken.",
|
||||||
"give": "Geef [Persoon] [Aantal] van jouw eigen Didier Dinks.",
|
"give": "Geeft [Persoon] [Aantal] van jouw eigen Didier Dinks.",
|
||||||
"google": "Geeft de eerste 10 zoekresultaten voor [Query] op Google.",
|
"google": "Geeft de eerste 10 zoekresultaten voor [Query] op Google.",
|
||||||
"hangman": "Raad [Letter]. Indien je geen letter opgeeft, toont het de status van de huidige Hangman game indien er een bezig is.",
|
"hangman": "Raad [Letter]. Indien je geen letter opgeeft, toont het de status van de huidige Hangman game indien er een bezig is.",
|
||||||
"hangman start": "Start een nieuwe Hangman game indien er nog geen bezig is. Indien je geen woord opgeeft, wordt er een willekeurig woord gekozen.\n**Indien je wel een woord opgeeft, werkt dit enkel in DM.**",
|
"hangman start": "Start een nieuwe Hangman game indien er nog geen bezig is. Indien je geen woord opgeeft, wordt er een willekeurig woord gekozen.\n**Indien je wel een woord opgeeft, werkt dit enkel in DM.**",
|
||||||
"hangman guess": "Probeer het woord te raden.",
|
"hangman guess": "Probeer het woord te raden.",
|
||||||
"claim": "Claim [Aantal] Didier Dinks uit je profit.\nIndien je geen aantal opgeeft (of \"all\"), claim je alles, inclusief je investering.",
|
"claim": "Claim [Aantal] Didier Dinks uit je profit.\nIndien je geen aantal opgeeft (of \"all\"), claim je alles, inclusief je investering.",
|
||||||
"inspire": "Generate quotes via [InspiroBot](https://inspirobot.me/).",
|
"inspire": "Genereer quotes via [InspiroBot](https://inspirobot.me/).",
|
||||||
"inventory": "Bekijk de items in jouw inventory.",
|
"inventory": "Bekijk de items in jouw inventory.",
|
||||||
"invest": "Investeer [Aantal] Didier Dinks in jouw Didier Bank om rente te vergaren.",
|
"invest": "Investeer [Aantal] Didier Dinks in jouw Didier Bank om rente te vergaren.",
|
||||||
|
"join": "Laat Didier [Thread] joinen.",
|
||||||
"jpl": "Informatie over de Jupiler Pro League.",
|
"jpl": "Informatie over de Jupiler Pro League.",
|
||||||
"jpl matches": "Bekijk de wedstrijden die gespeeld worden op [Week]. Default naar de huidige speeldag.",
|
"jpl matches": "Bekijk de wedstrijden die gespeeld worden op [Week]. Default naar de huidige speeldag.",
|
||||||
"jpl table": "De huidige stand van het klassement.",
|
"jpl table": "De huidige stand van het klassement.",
|
||||||
"jpl update": "Haalt de nieuwe code voor de competitie van dit jaar op.",
|
"jpl update": "Haalt de nieuwe code voor de competitie van dit jaar op.",
|
||||||
"leaderboard": "Bekijk de Top 10 van [Categorie].\nIndien je geen categorie opgeeft krijg je een lijst van categorieën.",
|
"leaderboard": "Bekijk de Top 10 van [Categorie].\nIndien je geen categorie opgeeft krijg je een lijst van categorieën.",
|
||||||
"les": "Bekijk het lessenrooster voor [Dag].\nIndien je geen dag opgeeft, is dit standaard vandaag.\nLes Morgen/Overmorgen werkt ook.",
|
"les": "Bekijk het lessenrooster voor [Dag].\nIndien je geen dag opgeeft, is dit standaard vandaag.\nLes Morgen/Overmorgen werkt ook.",
|
||||||
"lmgtfy": "Stuur iemand een LMGTFY link wanneer ze je een domme vraag stellen in plaats van het zelf op te zoeken.\nQueries met spaties moeten **niet** tussen aanhalingstekens staan.",
|
"link": "Stuurt de link die overeenkomt met [Naam].",
|
||||||
|
"lmgtfy": "Stuurt iemand een LMGTFY link wanneer ze je een domme vraag stellen in plaats van het zelf op te zoeken.\nQueries met spaties moeten **niet** tussen aanhalingstekens staan.",
|
||||||
"load": "Laadt [Cog] in.",
|
"load": "Laadt [Cog] in.",
|
||||||
"load all": "Laadt alle cogs in.",
|
"load all": "Laadt alle cogs in.",
|
||||||
"lost": "Bekijk het aantal Didier Dinks die al verloren zijn door gambling.",
|
"lost": "Bekijk het aantal Didier Dinks die al verloren zijn door gambling.",
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"DERP": "https://github.ugent.be/DERP"
|
||||||
|
}
|
|
@ -0,0 +1,208 @@
|
||||||
|
{
|
||||||
|
"semester_start": [14, 2, 2022],
|
||||||
|
"semester_end": [20, 2, 2022],
|
||||||
|
"holidays": [
|
||||||
|
{
|
||||||
|
"start_date": [18, 3, 2022, 0, 0, 0],
|
||||||
|
"end_date": [18, 3, 2022, 23, 59, 59]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"start_date": [1, 4, 2022, 11, 30, 0],
|
||||||
|
"end_date": [17, 4, 2022, 23, 59, 59]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"start_date": [18, 4, 2022, 0, 0, 0],
|
||||||
|
"end_date": [18, 4, 2022, 23, 59, 59]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"minors": [
|
||||||
|
{
|
||||||
|
"name": "Beveiliging en parallele systemen",
|
||||||
|
"role": 891744461405687808,
|
||||||
|
"schedule": [
|
||||||
|
{
|
||||||
|
"course": "Informatiebeveiliging",
|
||||||
|
"online_links": {
|
||||||
|
"opencast": "https://elosp.ugent.be/opencastplanner/live/GCMobile-ocrec79"
|
||||||
|
},
|
||||||
|
"slots": [
|
||||||
|
{
|
||||||
|
"online": "opencast",
|
||||||
|
"location": {
|
||||||
|
"campus": "Ardoyen",
|
||||||
|
"building": "iGent 126",
|
||||||
|
"room": "Auditorium 1"
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"day": "maandag",
|
||||||
|
"start": 1300,
|
||||||
|
"end": 1430
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"online": "opencast",
|
||||||
|
"location": {
|
||||||
|
"campus": "Ardoyen",
|
||||||
|
"building": "iGent 126",
|
||||||
|
"room": "Auditorium 1"
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"day": "dinsdag",
|
||||||
|
"start": 830,
|
||||||
|
"end": 1130
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Elektrotechniek en Telecommunicatie",
|
||||||
|
"role": 891744390035415111,
|
||||||
|
"schedule": [
|
||||||
|
{
|
||||||
|
"course": "Elektrotechniek",
|
||||||
|
"slots": [
|
||||||
|
{
|
||||||
|
"location": {
|
||||||
|
"campus": "Sterre",
|
||||||
|
"building": "S9",
|
||||||
|
"room": "3.2"
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"day": "dinsdag",
|
||||||
|
"start": 1300,
|
||||||
|
"end": 1430
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"location": {
|
||||||
|
"campus": "Sterre",
|
||||||
|
"building": "S8",
|
||||||
|
"room": "3.1"
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"day": "woensdag",
|
||||||
|
"start": 1430,
|
||||||
|
"end": 1730
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"location": {
|
||||||
|
"campus": "Sterre",
|
||||||
|
"building": "S9",
|
||||||
|
"room": "3.2"
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"day": "donderdag",
|
||||||
|
"start": 1130,
|
||||||
|
"end": 1300
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"schedule": [
|
||||||
|
{
|
||||||
|
"course": "Automaten, Berekenbaarheid en Complexiteit",
|
||||||
|
"slots": [
|
||||||
|
{
|
||||||
|
"location": {
|
||||||
|
"campus": "Sterre",
|
||||||
|
"building": "S25",
|
||||||
|
"room": "Emmy Noether"
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"day": "dinsdag",
|
||||||
|
"start": 1430,
|
||||||
|
"end": 1730
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"location": {
|
||||||
|
"campus": "Sterre",
|
||||||
|
"building": "S9",
|
||||||
|
"room": "PC 3.1 Konrad Zuse"
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"day": "donderdag",
|
||||||
|
"start": 1430,
|
||||||
|
"end": 1730
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"course": "Computationele Biologie",
|
||||||
|
"slots": [
|
||||||
|
{
|
||||||
|
"location": {
|
||||||
|
"campus": "Sterre",
|
||||||
|
"building": "S9",
|
||||||
|
"room": "3.2"
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"day": "woensdag",
|
||||||
|
"start": 830,
|
||||||
|
"end": 1130
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"location": {
|
||||||
|
"campus": "Sterre",
|
||||||
|
"building": "S9",
|
||||||
|
"room": "PC 3.1 Konrad Zuse"
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"day": "vrijdag",
|
||||||
|
"start": 830,
|
||||||
|
"end": 1130
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"course": "Logisch Programmeren",
|
||||||
|
"online_links": {
|
||||||
|
"zoom": "https://ufora.ugent.be/d2l/ext/rp/443368/lti/framedlaunch/556e197e-e87b-4c27-be5d-53adc7a41826"
|
||||||
|
},
|
||||||
|
"slots": [
|
||||||
|
{
|
||||||
|
"online": "zoom",
|
||||||
|
"time": {
|
||||||
|
"day": "maandag",
|
||||||
|
"start": 1430,
|
||||||
|
"end": 1730
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"online": "zoom",
|
||||||
|
"time": {
|
||||||
|
"day": "donderdag",
|
||||||
|
"start": 830,
|
||||||
|
"end": 1130
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"course": "Software Engineering Lab 2",
|
||||||
|
"slots": [
|
||||||
|
{
|
||||||
|
"location": {
|
||||||
|
"campus": "Sterre",
|
||||||
|
"building": "S8",
|
||||||
|
"room": "3.1"
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"day": "vrijdag",
|
||||||
|
"start": 1430,
|
||||||
|
"end": 1730
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -10,11 +10,13 @@ def generate(meme: Meme, fields):
|
||||||
"""
|
"""
|
||||||
Main function that takes a Meme as input & generates an image.
|
Main function that takes a Meme as input & generates an image.
|
||||||
"""
|
"""
|
||||||
|
fields = list(fields)
|
||||||
|
|
||||||
# If there's only one field, the user isn't required to use quotes
|
# If there's only one field, the user isn't required to use quotes
|
||||||
if meme.fields == 1:
|
if meme.fields == 1:
|
||||||
fields = [" ".join(fields)]
|
fields = [" ".join(fields)]
|
||||||
|
|
||||||
fields = _applyMeme(meme, fields)
|
fields = _apply_meme(meme, fields)
|
||||||
|
|
||||||
# List of fields to send to the API
|
# List of fields to send to the API
|
||||||
boxes = [{"text": ""}, {"text": ""}, {"text": ""}, {"text": ""}]
|
boxes = [{"text": ""}, {"text": ""}, {"text": ""}, {"text": ""}]
|
||||||
|
@ -33,19 +35,22 @@ def generate(meme: Meme, fields):
|
||||||
return {"success": False, "message": "Er is een fout opgetreden."}
|
return {"success": False, "message": "Er is een fout opgetreden."}
|
||||||
|
|
||||||
# Post meme
|
# Post meme
|
||||||
reply = _postMeme(meme, boxes)
|
reply = _post_meme(meme, boxes)
|
||||||
|
|
||||||
# Adding a message parameter makes the code in the cog a lot cleaner
|
# Adding a message parameter makes the code in the cog a lot cleaner
|
||||||
if not reply["success"]:
|
if not reply["success"]:
|
||||||
|
reply["success"] = False
|
||||||
reply["message"] = "Error! Controleer of je de juiste syntax hebt gebruikt. Gebruik het commando " \
|
reply["message"] = "Error! Controleer of je de juiste syntax hebt gebruikt. Gebruik het commando " \
|
||||||
"\"memes\" voor een lijst aan geaccepteerde meme-namen."
|
"\"memes\" voor een lijst aan geaccepteerde meme-namen."
|
||||||
else:
|
else:
|
||||||
reply["message"] = reply["data"]["url"]
|
reply["message"] = reply["data"]["url"]
|
||||||
|
|
||||||
|
reply["success"] = False
|
||||||
|
|
||||||
return reply
|
return reply
|
||||||
|
|
||||||
|
|
||||||
def _postMeme(meme: Meme, boxes):
|
def _post_meme(meme: Meme, boxes):
|
||||||
"""
|
"""
|
||||||
Performs API request to generate the meme
|
Performs API request to generate the meme
|
||||||
"""
|
"""
|
||||||
|
@ -65,7 +70,7 @@ def _postMeme(meme: Meme, boxes):
|
||||||
return memeReply
|
return memeReply
|
||||||
|
|
||||||
|
|
||||||
def _applyMeme(meme: Meme, fields):
|
def _apply_meme(meme: Meme, fields):
|
||||||
"""
|
"""
|
||||||
Some memes are in a special format that only requires
|
Some memes are in a special format that only requires
|
||||||
a few words to be added, or needs the input to be changed.
|
a few words to be added, or needs the input to be changed.
|
||||||
|
@ -74,9 +79,11 @@ def _applyMeme(meme: Meme, fields):
|
||||||
Links certain meme id's to functions that need to be applied first.
|
Links certain meme id's to functions that need to be applied first.
|
||||||
"""
|
"""
|
||||||
memeDict = {
|
memeDict = {
|
||||||
102156234: _mockingSpongebob,
|
102156234: mocking_spongebob,
|
||||||
91538330: _xXEverywhere,
|
91538330: _x_x_everywhere,
|
||||||
252600902: _alwaysHasBeen
|
252600902: _always_has_been,
|
||||||
|
167754325: _math_is_math,
|
||||||
|
206493414: _i_used_the_x_to_destroy_the_x
|
||||||
}
|
}
|
||||||
|
|
||||||
# Meme needs no special treatment
|
# Meme needs no special treatment
|
||||||
|
@ -86,17 +93,29 @@ def _applyMeme(meme: Meme, fields):
|
||||||
return memeDict[meme.meme_id](fields)
|
return memeDict[meme.meme_id](fields)
|
||||||
|
|
||||||
|
|
||||||
def _mockingSpongebob(fields):
|
def mocking_spongebob(fields):
|
||||||
return list(map(mock, fields))
|
return list(map(mock, fields))
|
||||||
|
|
||||||
|
|
||||||
def _xXEverywhere(fields):
|
def _x_x_everywhere(fields):
|
||||||
word = fields[0]
|
word = fields[0]
|
||||||
|
|
||||||
return ["{}".format(word), "{} everywhere".format(word)]
|
return ["{}".format(word), "{} everywhere".format(word)]
|
||||||
|
|
||||||
|
|
||||||
def _alwaysHasBeen(fields):
|
def _always_has_been(fields):
|
||||||
word = fields[0]
|
word = fields[0]
|
||||||
|
|
||||||
return ["Wait, it's all {}?".format(word), "Always has been"]
|
return ["Wait, {}?".format(word), "Always has been"]
|
||||||
|
|
||||||
|
|
||||||
|
def _math_is_math(fields):
|
||||||
|
word = fields[0].upper()
|
||||||
|
|
||||||
|
return ["", f"{word} IS {word}!"]
|
||||||
|
|
||||||
|
|
||||||
|
def _i_used_the_x_to_destroy_the_x(fields):
|
||||||
|
word = fields[0]
|
||||||
|
|
||||||
|
return ["", f"I used the {word} to destroy the {word}"]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
|
from discord import Interaction
|
||||||
from discord.ext.commands import Context
|
from discord.ext.commands import Context
|
||||||
from dislash import SlashInteraction
|
|
||||||
|
|
||||||
|
|
||||||
def title_case(string):
|
def title_case(string):
|
||||||
|
@ -42,16 +42,15 @@ def format_command_usage(ctx: Context) -> str:
|
||||||
return f"{ctx.author.display_name} in {_format_error_location(ctx)}: {ctx.message.content}"
|
return f"{ctx.author.display_name} in {_format_error_location(ctx)}: {ctx.message.content}"
|
||||||
|
|
||||||
|
|
||||||
def format_slash_command_usage(interaction: SlashInteraction) -> str:
|
def format_slash_command_usage(interaction: Interaction) -> str:
|
||||||
# Create a string with the options used
|
# Create a string with the options used
|
||||||
# TODO look into the format used by the lib because it's terrible
|
|
||||||
options = " ".join(list(map(
|
options = " ".join(list(map(
|
||||||
lambda option: f"{option.name}: \"{option.value}\"",
|
lambda o: f"{o['name']}: \"{o['value']}\"",
|
||||||
interaction.data.options.values()
|
interaction.data.get("options", [])
|
||||||
)))
|
)))
|
||||||
|
|
||||||
command = f"{interaction.slash_command.name} {options or ''}"
|
command = f"{interaction.data['name']} {options or ''}"
|
||||||
return f"{interaction.author.display_name} in {_format_error_location(interaction)}: /{command}"
|
return f"{interaction.user.display_name} in {_format_error_location(interaction)}: /{command}"
|
||||||
|
|
||||||
|
|
||||||
def get_edu_year(index: int) -> str:
|
def get_edu_year(index: int) -> str:
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
from typing import Union, Optional
|
||||||
|
|
||||||
|
import discord
|
||||||
|
from discord import ApplicationContext
|
||||||
|
from discord.ext.commands import Context
|
||||||
|
|
||||||
|
from data import constants
|
||||||
|
|
||||||
|
|
||||||
|
def get_display_name(ctx: Union[ApplicationContext, Context], user_id: int) -> str:
|
||||||
|
author = ctx.author if isinstance(ctx, Context) else ctx.user
|
||||||
|
|
||||||
|
# Check if this is a DM, or the user is not in the guild
|
||||||
|
if ctx.guild is None or ctx.guild.get_member(user_id) is None:
|
||||||
|
# User is the author, no need to fetch their name
|
||||||
|
if user_id == author.id:
|
||||||
|
return author.display_name
|
||||||
|
|
||||||
|
# Get member instance from CoC
|
||||||
|
COC = ctx.bot.get_guild(int(constants.DeZandbak))
|
||||||
|
member = COC.get_member(user_id)
|
||||||
|
if member is not None:
|
||||||
|
return member.display_name
|
||||||
|
|
||||||
|
# Try to fetch the user
|
||||||
|
user = ctx.bot.get_user(user_id)
|
||||||
|
if user is not None:
|
||||||
|
return user.name
|
||||||
|
|
||||||
|
# User couldn't be found
|
||||||
|
return f"[? | {user_id}]"
|
||||||
|
|
||||||
|
mem = ctx.guild.get_member(user_id)
|
||||||
|
return mem.display_name
|
||||||
|
|
||||||
|
|
||||||
|
async def reply_to_reference(ctx: Context, content: Optional[str] = None, embed: Optional[discord.Embed] = None, always_mention=False):
|
||||||
|
"""Reply to a message
|
||||||
|
In case the message is a reply to another message, try to reply to that one instead and ping the author
|
||||||
|
otherwise, reply to the message that invoked the command & only mention the author if necessary
|
||||||
|
"""
|
||||||
|
# Message is a reply
|
||||||
|
if ctx.message.reference is not None:
|
||||||
|
cached = ctx.message.reference.cached_message
|
||||||
|
|
||||||
|
# Reference is not cached anymore: fetch it
|
||||||
|
if cached is None:
|
||||||
|
# Message is in the same channel, otherwise no way to reply to it
|
||||||
|
cached = await ctx.channel.fetch_message(ctx.message.reference.message_id)
|
||||||
|
|
||||||
|
return await cached.reply(content, embed=embed, mention_author=cached.author != ctx.author)
|
||||||
|
|
||||||
|
return await ctx.reply(content, embed=embed, mention_author=always_mention)
|
|
@ -1,8 +1,8 @@
|
||||||
|
# Beta version of Discord.py fork
|
||||||
|
py-cord==2.0.0b1
|
||||||
|
|
||||||
python-dotenv==0.14.0
|
python-dotenv==0.14.0
|
||||||
beautifulsoup4==4.9.1
|
beautifulsoup4==4.9.1
|
||||||
discord.py==1.7.3
|
|
||||||
git+https://github.com/Rapptz/discord-ext-menus@master
|
|
||||||
discord-ext-ipc==2.0.0
|
|
||||||
psycopg2==2.8.5
|
psycopg2==2.8.5
|
||||||
psycopg2-binary==2.8.5
|
psycopg2-binary==2.8.5
|
||||||
python-dateutil==2.6.1
|
python-dateutil==2.6.1
|
||||||
|
@ -13,12 +13,7 @@ tabulate==0.8.7
|
||||||
yarl==1.4.2
|
yarl==1.4.2
|
||||||
feedparser==6.0.2
|
feedparser==6.0.2
|
||||||
googletrans==4.0.0rc1
|
googletrans==4.0.0rc1
|
||||||
quart==0.15.1
|
|
||||||
Quart-CORS==0.5.0
|
|
||||||
attrs~=21.2.0
|
attrs~=21.2.0
|
||||||
dacite~=1.6.0
|
dacite~=1.6.0
|
||||||
pytest==6.2.4
|
pytest==6.2.4
|
||||||
markdownify==0.9.2
|
markdownify==0.9.2
|
||||||
|
|
||||||
# Experimental package for slash commands & menus
|
|
||||||
dislash.py==1.4.9
|
|
|
@ -4,7 +4,7 @@ from dotenv import load_dotenv
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv(verbose=True)
|
||||||
|
|
||||||
|
|
||||||
def _to_bool(value: str) -> bool:
|
def _to_bool(value: str) -> bool:
|
||||||
|
@ -31,7 +31,6 @@ DB_NAME = os.getenv("DBNAME", "")
|
||||||
|
|
||||||
# Discord-related
|
# Discord-related
|
||||||
TOKEN = os.getenv("TOKEN", "")
|
TOKEN = os.getenv("TOKEN", "")
|
||||||
HOST_IPC = _to_bool(os.getenv("HOSTIPC", "false"))
|
|
||||||
READY_MESSAGE = os.getenv("READYMESSAGE", "I'M READY I'M READY I'M READY I'M READY") # Yes, this is a Spongebob reference
|
READY_MESSAGE = os.getenv("READYMESSAGE", "I'M READY I'M READY I'M READY I'M READY") # Yes, this is a Spongebob reference
|
||||||
STATUS_MESSAGE = os.getenv("STATUSMESSAGE", "with your Didier Dinks.")
|
STATUS_MESSAGE = os.getenv("STATUSMESSAGE", "with your Didier Dinks.")
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
from data.snipe import Snipe
|
from data.snipe import Snipe
|
||||||
from discord.ext import commands, ipc
|
from discord.ext import commands
|
||||||
from dislash import InteractionClient
|
|
||||||
import os
|
import os
|
||||||
from settings import HOST_IPC, SLASH_TEST_GUILDS
|
|
||||||
from startup.init_files import check_all
|
from startup.init_files import check_all
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
|
@ -11,30 +9,18 @@ class Didier(commands.Bot):
|
||||||
"""
|
"""
|
||||||
Main Bot class for Didier
|
Main Bot class for Didier
|
||||||
"""
|
"""
|
||||||
# Reference to interactions client
|
|
||||||
interactions: InteractionClient
|
|
||||||
|
|
||||||
# Dict to store the most recent Snipe info per channel
|
# Dict to store the most recent Snipe info per channel
|
||||||
snipe: Dict[int, Snipe] = {}
|
snipe: Dict[int, Snipe] = {}
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self._host_ipc = HOST_IPC
|
|
||||||
|
|
||||||
# IPC Server
|
|
||||||
# TODO secret key
|
|
||||||
self.ipc = ipc.Server(self, secret_key="SOME_SECRET_KEY") if self._host_ipc else None
|
|
||||||
|
|
||||||
# Cogs that should be loaded before the others
|
# Cogs that should be loaded before the others
|
||||||
self._preload = ("ipc", "utils", "failedchecks", "events",)
|
self._preload = ("utils", "failedchecks", "events",)
|
||||||
|
|
||||||
# Remove default help command
|
# Remove default help command
|
||||||
self.remove_command("help")
|
self.remove_command("help")
|
||||||
|
|
||||||
# Create interactions client
|
|
||||||
self.interactions = InteractionClient(self, test_guilds=SLASH_TEST_GUILDS)
|
|
||||||
|
|
||||||
# Load all extensions
|
# Load all extensions
|
||||||
self.init_extensions()
|
self.init_extensions()
|
||||||
|
|
||||||
|
@ -65,9 +51,3 @@ class Didier(commands.Bot):
|
||||||
# Subdirectory
|
# Subdirectory
|
||||||
# Also walrus operator hype
|
# Also walrus operator hype
|
||||||
self._init_directory(new_path)
|
self._init_directory(new_path)
|
||||||
|
|
||||||
async def on_ipc_ready(self):
|
|
||||||
print("IPC server is ready.")
|
|
||||||
|
|
||||||
async def on_ipc_error(self, endpoint, error):
|
|
||||||
print(endpoint, "raised", error)
|
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from data.courses import load_courses, find_course_from_name
|
||||||
|
|
||||||
|
|
||||||
|
class TestCourses(unittest.TestCase):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.courses = load_courses()
|
||||||
|
|
||||||
|
def test_find_course(self):
|
||||||
|
self.assertIsNone(find_course_from_name("garbage", self.courses))
|
||||||
|
self.assertIsNone(find_course_from_name("garbage"))
|
||||||
|
|
||||||
|
# Find by name
|
||||||
|
webdev = find_course_from_name("Webdevelopment", self.courses)
|
||||||
|
self.assertIsNotNone(webdev)
|
||||||
|
self.assertEqual(webdev.code, "C003779")
|
||||||
|
|
||||||
|
# Find by abbreviation
|
||||||
|
infosec = find_course_from_name("infosec", self.courses)
|
||||||
|
self.assertIsNotNone(infosec)
|
||||||
|
self.assertEqual(infosec.code, "E019400")
|
||||||
|
|
||||||
|
# Case sensitive
|
||||||
|
not_found = find_course_from_name("ad3", self.courses, case_insensitive=False)
|
||||||
|
self.assertIsNone(not_found)
|
||||||
|
|
||||||
|
# Find by alt name
|
||||||
|
pcs = find_course_from_name("parallel computer systems", self.courses)
|
||||||
|
self.assertIsNotNone(pcs)
|
||||||
|
self.assertEqual(pcs.code, "E034140")
|
Loading…
Reference in New Issue