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__
|
||||
.env
|
||||
/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.snipe import Snipe, Action, should_snipe
|
||||
import datetime
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from dislash.application_commands.errors import InteractionCheckFailure
|
||||
from functions import checks, easterEggResponses, stringFormatters
|
||||
from functions.database import stats, muttn, custom_commands, commands as command_stats
|
||||
import pytz
|
||||
|
@ -79,6 +78,12 @@ class Events(commands.Cog):
|
|||
# Earn XP & Message count
|
||||
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()
|
||||
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
|
||||
if hasattr(ctx.command, 'on_error'):
|
||||
return
|
||||
|
||||
# Someone just mentioned Didier without calling a real command,
|
||||
# don't care about this error
|
||||
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.")
|
||||
elif isinstance(err, (commands.ChannelNotFound, commands.ChannelNotReadable)):
|
||||
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
|
||||
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:
|
||||
usage = stringFormatters.format_command_usage(ctx)
|
||||
await self.sendErrorEmbed(err, "Command", usage)
|
||||
|
||||
@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
|
||||
"""
|
||||
if not interaction.is_command():
|
||||
return
|
||||
|
||||
print(stringFormatters.format_slash_command_usage(interaction))
|
||||
|
||||
command_stats.invoked(command_stats.InvocationType.SlashCommand)
|
||||
|
||||
@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
|
||||
if self.client.user.id != int(constants.didierId):
|
||||
raise err
|
||||
|
||||
if isinstance(err, InteractionCheckFailure):
|
||||
return await interaction.reply("Je hebt geen toegang tot dit commando.", ephemeral=True)
|
||||
if isinstance(err, commands.CheckFailure):
|
||||
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)
|
||||
|
||||
@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 random
|
||||
|
||||
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):
|
||||
|
@ -98,17 +99,10 @@ class Fun(commands.Cog):
|
|||
if result is None:
|
||||
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)
|
||||
|
||||
# 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
|
||||
await ctx.send(generated["message"])
|
||||
await ctx.reply(generated["message"], mention_author=False)
|
||||
|
||||
@commands.command(name="Memes")
|
||||
@commands.check(checks.allowedChannels)
|
||||
|
@ -118,15 +112,7 @@ class Fun(commands.Cog):
|
|||
Command that shows a list of memes in the database.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
memeList = memes.getAllMemes()
|
||||
|
||||
# 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)
|
||||
return await MemesList(ctx=ctx).send()
|
||||
|
||||
@commands.command(name="Pjoke")
|
||||
@help.Category(category=Category.Fun)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from data import constants
|
||||
import discord
|
||||
from discord.commands import SlashCommand
|
||||
from discord.ext import commands
|
||||
from enums.help_categories import categories, getCategory, Category
|
||||
import json
|
||||
|
@ -49,7 +50,7 @@ class HelpCommand(commands.MinimalHelpCommand):
|
|||
return await self.send_bot_help(self.get_bot_mapping())
|
||||
|
||||
# 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:
|
||||
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
|
||||
from discord.ext import commands
|
||||
|
||||
from data.menus import leaderboards
|
||||
from decorators import help
|
||||
from enums.help_categories import Category
|
||||
from enums.numbers import Numbers
|
||||
from functions import checks, xp
|
||||
from functions.database import currency, stats, poke, muttn
|
||||
import math
|
||||
import requests
|
||||
from functions import checks
|
||||
|
||||
|
||||
# TODO some sort of general leaderboard because all of them are the same
|
||||
class Leaderboards(commands.Cog):
|
||||
|
||||
def __init__(self, client):
|
||||
|
@ -18,7 +14,7 @@ class Leaderboards(commands.Cog):
|
|||
self.utilsCog = self.client.get_cog("Utils")
|
||||
|
||||
# Don't allow any commands to work when locked
|
||||
def cog_check(self, ctx):
|
||||
def cog_check(self, _):
|
||||
return not self.client.locked
|
||||
|
||||
@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)
|
||||
async def dinks(self, ctx):
|
||||
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
|
||||
|
||||
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")
|
||||
lb = leaderboards.DinksLeaderboard(ctx=ctx)
|
||||
await lb.send()
|
||||
|
||||
@leaderboard.command(name="Corona", hidden=True)
|
||||
async def corona(self, ctx):
|
||||
result = requests.get("http://corona.lmao.ninja/v2/countries").json()
|
||||
result.sort(key=lambda x: int(x["cases"]), reverse=True)
|
||||
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())
|
||||
lb = leaderboards.CoronaLeaderboard(ctx=ctx)
|
||||
await lb.send()
|
||||
|
||||
@leaderboard.command(name="Bitcoin", aliases=["Bc"], hidden=True)
|
||||
async def bitcoin(self, ctx):
|
||||
users = currency.getAllRows()
|
||||
boardTop = []
|
||||
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")
|
||||
lb = leaderboards.BitcoinLeaderboard(ctx=ctx)
|
||||
await lb.send()
|
||||
|
||||
@leaderboard.command(name="Rob", hidden=True)
|
||||
async def rob(self, ctx):
|
||||
users = list(stats.getAllRows())
|
||||
boardTop = []
|
||||
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")
|
||||
lb = leaderboards.RobLeaderboard(ctx=ctx)
|
||||
await lb.send()
|
||||
|
||||
@leaderboard.command(name="Poke", hidden=True)
|
||||
async def poke(self, ctx):
|
||||
s = stats.getAllRows()
|
||||
blacklist = poke.getAllBlacklistedUsers()
|
||||
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")
|
||||
lb = leaderboards.PokeLeaderboard(ctx=ctx)
|
||||
await lb.send()
|
||||
|
||||
@leaderboard.command(name="Xp", aliases=["Level"], hidden=True)
|
||||
async def xp(self, ctx):
|
||||
s = stats.getAllRows()
|
||||
boardTop = []
|
||||
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")
|
||||
lb = leaderboards.XPLeaderboard(ctx=ctx)
|
||||
await lb.send()
|
||||
|
||||
@leaderboard.command(name="Messages", aliases=["Mc", "Mess"], hidden=True)
|
||||
async def messages(self, ctx):
|
||||
s = stats.getAllRows()
|
||||
boardTop = []
|
||||
|
||||
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")
|
||||
lb = leaderboards.MessageLeaderboard(ctx=ctx)
|
||||
await lb.send()
|
||||
|
||||
@leaderboard.command(name="Muttn", aliases=["M", "Mutn", "Mutten"], hidden=True)
|
||||
async def muttn(self, ctx):
|
||||
users = muttn.getAllRows()
|
||||
boardTop = []
|
||||
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")
|
||||
lb = leaderboards.MuttnLeaderboard(ctx=ctx)
|
||||
await lb.send()
|
||||
|
||||
async def callLeaderboard(self, name, ctx):
|
||||
await [command for command in self.leaderboard.commands if command.name.lower() == name.lower()][0](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)
|
||||
command = [command for command in self.leaderboard.commands if command.name.lower() == name.lower()][0]
|
||||
await command(ctx)
|
||||
|
||||
|
||||
def setup(client):
|
||||
|
|
|
@ -151,7 +151,7 @@ class Oneliners(commands.Cog):
|
|||
@commands.command(name="Inspire")
|
||||
@help.Category(Category.Other)
|
||||
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:
|
||||
await ctx.send(image.text)
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import discord
|
||||
from discord.ext import commands
|
||||
|
||||
from data.embeds.snipe import EditSnipe, DeleteSnipe
|
||||
from data.links import get_link_for
|
||||
from data.menus import custom_commands
|
||||
from data.snipe import Action, Snipe
|
||||
from decorators import help
|
||||
from enums.help_categories import Category
|
||||
from functions.database.custom_commands import get_all
|
||||
from functions.stringFormatters import capitalize
|
||||
from functions.utils import reply_to_reference
|
||||
from startup.didier import Didier
|
||||
|
||||
|
||||
|
@ -14,10 +15,22 @@ class Other(commands.Cog):
|
|||
def __init__(self, client: Didier):
|
||||
self.client: Didier = client
|
||||
|
||||
# TODO add locked field to Didier instead of client
|
||||
# # Don't allow any commands to work when locked
|
||||
# def cog_check(self, ctx):
|
||||
# return not self.client.locked
|
||||
# Don't allow any commands to work when locked
|
||||
def cog_check(self, _):
|
||||
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")
|
||||
@help.Category(category=Category.Didier)
|
||||
|
@ -25,10 +38,17 @@ class Other(commands.Cog):
|
|||
"""
|
||||
Get a list of all custom commands
|
||||
"""
|
||||
all_commands = get_all()
|
||||
formatted = list(sorted(map(lambda x: capitalize(x["name"]), all_commands)))
|
||||
src = custom_commands.CommandsList(formatted)
|
||||
await custom_commands.Pages(source=src, clear_reactions_after=True).start(ctx)
|
||||
await custom_commands.CommandsList(ctx).send()
|
||||
|
||||
@commands.command(name="Join", usage="[Thread]")
|
||||
@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")
|
||||
@help.Category(category=Category.Other)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from data import schedule
|
||||
from data.courses import find_course_from_name
|
||||
from data.embeds.deadlines import Deadlines
|
||||
from data.embeds.food import Menu
|
||||
from decorators import help
|
||||
|
@ -8,6 +9,7 @@ from enums.help_categories import Category
|
|||
from functions import config, les
|
||||
from functions.stringFormatters import capitalize
|
||||
from functions.timeFormatters import skip_weekends
|
||||
from functions.utils import reply_to_reference
|
||||
|
||||
|
||||
class School(commands.Cog):
|
||||
|
@ -82,9 +84,28 @@ class School(commands.Cog):
|
|||
if message.is_system():
|
||||
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("✅")
|
||||
|
||||
@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"])
|
||||
@help.Category(category=Category.School)
|
||||
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 dislash import SlashInteraction, slash_command, Option, OptionType
|
||||
from discord.commands import slash_command, ApplicationContext, Option
|
||||
|
||||
from data.embeds.urban_dictionary import Definition
|
||||
from startup.didier import Didier
|
||||
|
@ -9,15 +9,10 @@ class DefineSlash(commands.Cog):
|
|||
def __init__(self, client: Didier):
|
||||
self.client: Didier = client
|
||||
|
||||
@slash_command(name="define",
|
||||
description="Urban Dictionary",
|
||||
options=[
|
||||
Option("query", "Search query", OptionType.STRING, required=True)
|
||||
]
|
||||
)
|
||||
async def _define_slash(self, interaction: SlashInteraction, query):
|
||||
@slash_command(name="define", description="Urban Dictionary")
|
||||
async def _define_slash(self, ctx: ApplicationContext, query: Option(str, "Search query", required=True)):
|
||||
embed = Definition(query).to_embed()
|
||||
await interaction.reply(embed=embed)
|
||||
await ctx.respond(embed=embed)
|
||||
|
||||
|
||||
def setup(client: Didier):
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from discord.ext import commands
|
||||
from dislash import SlashInteraction, slash_command, Option, OptionType
|
||||
from functions import config, checks
|
||||
from discord.commands import Option, SlashCommandGroup, ApplicationContext, permissions
|
||||
from functions import config
|
||||
from functions.football import get_matches, get_table, get_jpl_code
|
||||
from startup.didier import Didier
|
||||
|
||||
|
@ -9,35 +9,29 @@ class FootballSlash(commands.Cog):
|
|||
def __init__(self, client: Didier):
|
||||
self.client: Didier = client
|
||||
|
||||
@slash_command(name="jpl", description="Jupiler Pro League commands")
|
||||
async def _jpl_group(self, interaction: SlashInteraction):
|
||||
pass
|
||||
_jpl_group = SlashCommandGroup("jpl", "Jupiler Pro League commands")
|
||||
|
||||
@_jpl_group.sub_command(name="matches",
|
||||
description="Schema voor een bepaalde speeldag",
|
||||
options=[
|
||||
Option("day", "Speeldag (default huidige)", OptionType.INTEGER)
|
||||
]
|
||||
)
|
||||
async def _jpl_matches_slash(self, interaction: SlashInteraction, day: int = None):
|
||||
@_jpl_group.command(name="matches", description="Schema voor een bepaalde speeldag")
|
||||
async def _jpl_matches_slash(self, ctx: ApplicationContext,
|
||||
day: Option(int, name="day", description="Speeldag (default huidige)", required=False, default=None)
|
||||
):
|
||||
# Default is current day
|
||||
if day is None:
|
||||
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")
|
||||
async def _jpl_table_slash(self, interaction: SlashInteraction):
|
||||
await interaction.reply(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="table", description="Huidige rangschikking")
|
||||
async def _jpl_table_slash(self, ctx: ApplicationContext):
|
||||
await ctx.response.defer()
|
||||
await ctx.send_followup(get_table())
|
||||
|
||||
@_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()
|
||||
config.config("jpl", code)
|
||||
await interaction.reply(f"Done (code: {code})")
|
||||
await ctx.respond(f"Done (code: {code})")
|
||||
|
||||
|
||||
def setup(client: Didier):
|
||||
|
|
|
@ -1,28 +1,69 @@
|
|||
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.menus.memes import MemesList
|
||||
from functions.memes import generate
|
||||
from functions.stringFormatters import title_case
|
||||
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):
|
||||
def __init__(self, client: Didier):
|
||||
self.client: Didier = client
|
||||
|
||||
@slash_command(
|
||||
name="xkcd",
|
||||
description="Zoek xkcd comics",
|
||||
options=[
|
||||
Option(
|
||||
"num",
|
||||
description="Nummer van de comic (default de comic van vandaag).",
|
||||
type=OptionType.INTEGER,
|
||||
required=False
|
||||
)
|
||||
]
|
||||
)
|
||||
async def _xkcd_slash(self, interaction: SlashInteraction, num: int = None):
|
||||
return await interaction.reply(embed=XKCDEmbed(num).create())
|
||||
@slash_command(name="xkcd", description="Zoek xkcd comics")
|
||||
async def _xkcd_slash(self, ctx: ApplicationContext,
|
||||
num: Option(int, description="Nummer van de comic (default de comic van vandaag).", required=False, default=None)
|
||||
):
|
||||
return await ctx.respond(embed=XKCDEmbed(num).create())
|
||||
|
||||
@slash_command(name="memes", description="Lijst van memegen-memes")
|
||||
async def _memes_slash(self, ctx: ApplicationContext):
|
||||
return await MemesList(ctx=ctx).respond()
|
||||
|
||||
@slash_command(name="memegen", description="Genereer memes")
|
||||
async def _memegen_slash(self, ctx: ApplicationContext,
|
||||
meme: Option(str, description="Naam van de template", required=True, autocomplete=autocomplete_memes),
|
||||
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):
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
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 startup.didier import Didier
|
||||
|
||||
|
@ -8,20 +8,15 @@ class GoogleSlash(commands.Cog):
|
|||
def __init__(self, client: Didier):
|
||||
self.client: Didier = client
|
||||
|
||||
@slash_command(name="google",
|
||||
description="Google search",
|
||||
options=[
|
||||
Option("query", "Search query", OptionType.STRING, required=True)
|
||||
]
|
||||
)
|
||||
async def _google_slash(self, interaction: SlashInteraction, query: str):
|
||||
@slash_command(name="google", description="Google search")
|
||||
async def _google_slash(self, ctx: ApplicationContext, query: Option(str, "Search query")):
|
||||
result = google_search(query)
|
||||
|
||||
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)
|
||||
await interaction.reply(embed=embed)
|
||||
await ctx.respond(embed=embed)
|
||||
|
||||
|
||||
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 dislash import SlashInteraction, slash_command, Option, OptionType
|
||||
from discord.commands import slash_command, ApplicationContext, Option, AutocompleteContext
|
||||
|
||||
from data import schedule
|
||||
from data.courses import load_courses, find_course_from_name
|
||||
from data.embeds.food import Menu
|
||||
from data.embeds.deadlines import Deadlines
|
||||
from functions import les, config
|
||||
|
@ -10,66 +11,77 @@ from functions.timeFormatters import skip_weekends
|
|||
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):
|
||||
def __init__(self, client: Didier):
|
||||
self.client: Didier = client
|
||||
|
||||
@slash_command(
|
||||
name="eten",
|
||||
description="Menu in de UGENT resto's op een bepaalde dag",
|
||||
options=[
|
||||
Option(
|
||||
"dag",
|
||||
description="Dag",
|
||||
type=OptionType.STRING
|
||||
)
|
||||
]
|
||||
)
|
||||
async def _food_slash(self, interaction: SlashInteraction, dag: str = None):
|
||||
@slash_command(name="eten", description="Menu in de UGent resto's op een bepaalde dag")
|
||||
async def _food_slash(self, ctx: ApplicationContext,
|
||||
dag: Option(str, description="Dag", required=False, default=None, autocomplete=day_autocomplete)
|
||||
):
|
||||
embed = Menu(dag).to_embed()
|
||||
await interaction.reply(embed=embed)
|
||||
await ctx.respond(embed=embed)
|
||||
|
||||
@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()
|
||||
await interaction.reply(embed=embed)
|
||||
await ctx.respond(embed=embed)
|
||||
|
||||
@slash_command(
|
||||
name="les",
|
||||
description="Lessenrooster voor [Dag] (default vandaag)",
|
||||
options=[
|
||||
Option(
|
||||
"dag",
|
||||
description="dag",
|
||||
type=OptionType.STRING,
|
||||
required=False
|
||||
)
|
||||
]
|
||||
)
|
||||
async def _schedule_slash(self, interaction: SlashInteraction, day: str = None):
|
||||
@slash_command(name="les", description="Lessenrooster voor [Dag] (default vandaag)",)
|
||||
async def _schedule_slash(self, ctx: ApplicationContext,
|
||||
dag: Option(str, description="Dag", required=False, default=None, autocomplete=day_autocomplete)
|
||||
):
|
||||
"""It's late and I really don't want to refactor the original right now"""
|
||||
if day is not None:
|
||||
day = day.lower()
|
||||
if dag is not None:
|
||||
dag = dag.lower()
|
||||
|
||||
date = les.find_target_date(day)
|
||||
date = les.find_target_date(dag)
|
||||
|
||||
# Person explicitly requested a weekend-day
|
||||
if day is not None and day.lower() in ("morgen", "overmorgen") and date.weekday() > 4:
|
||||
return await interaction.reply(f"{capitalize(day)} is het weekend.", ephemeral=True)
|
||||
if dag is not None and dag.lower() in ("morgen", "overmorgen") and date.weekday() > 4:
|
||||
return await ctx.respond(f"{capitalize(dag)} is het weekend.", ephemeral=True)
|
||||
|
||||
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:
|
||||
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
|
||||
if interaction.guild is None:
|
||||
minor_roles = [*schedule.find_minor(self.client, interaction.author.id)]
|
||||
return await interaction.reply(embed=s.create_schedule(minor_roles=minor_roles).to_embed())
|
||||
if ctx.guild is None:
|
||||
minor_roles = [*schedule.find_minor(self.client, ctx.interaction.user.id)]
|
||||
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):
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
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 startup.didier import Didier
|
||||
|
@ -9,18 +9,14 @@ class TranslateSlash(commands.Cog):
|
|||
def __init__(self, client: Didier):
|
||||
self.client: Didier = client
|
||||
|
||||
@slash_command(
|
||||
name="translate",
|
||||
description="Google Translate",
|
||||
options=[
|
||||
Option("text", "Tekst om te vertalen", OptionType.STRING, required=True),
|
||||
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"):
|
||||
@slash_command(name="translate", description="Google Translate")
|
||||
async def _translate_slash(self, ctx: ApplicationContext,
|
||||
text: Option(str, description="Tekst om te vertalen"),
|
||||
from_lang: Option(str, description="Taal om van te vertalen (default auto-detect)", default="auto"),
|
||||
to_lang: Option(str, description="Taal om naar te vertalen (default NL)", default="nl")
|
||||
):
|
||||
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):
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
from converters.numbers import Abbreviated
|
||||
from data.menus import storePages
|
||||
from decorators import help
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
|
@ -22,11 +21,12 @@ class Store(commands.Cog):
|
|||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(Category.Currency)
|
||||
async def store(self, ctx):
|
||||
entries = store.getAllItems()
|
||||
await storePages.Pages(source=storePages.Source(entries), clear_reactions_after=True).start(ctx)
|
||||
pass
|
||||
# entries = store.getAllItems()
|
||||
# await storePages.Pages(source=storePages.Source(entries), clear_reactions_after=True).start(ctx)
|
||||
|
||||
@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:
|
||||
return
|
||||
|
||||
|
@ -56,7 +56,7 @@ class Store(commands.Cog):
|
|||
))
|
||||
|
||||
@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:
|
||||
return
|
||||
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 discord.ext import menus
|
||||
from typing import Union
|
||||
|
||||
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):
|
||||
def __init__(self, data, colour=discord.Colour.blue()):
|
||||
super().__init__(data, per_page=15)
|
||||
self.colour = colour
|
||||
class CommandsList(Paginated):
|
||||
def __init__(self, ctx: Union[ApplicationContext, Context]):
|
||||
all_commands = get_all()
|
||||
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):
|
||||
embed = discord.Embed(colour=self.colour)
|
||||
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)
|
||||
def format_entry(self, index: int, value: tuple) -> str:
|
||||
return value[0]
|
||||
|
|
|
@ -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
|
||||
from dotenv import load_dotenv
|
||||
from functions.prefixes import get_prefix
|
||||
from settings import STATUS_MESSAGE, TOKEN
|
||||
from startup.didier import Didier
|
||||
|
||||
if __name__ == "__main__":
|
||||
load_dotenv(verbose=True)
|
||||
|
||||
# Activities
|
||||
activity = discord.Activity(type=discord.ActivityType.playing, name=STATUS_MESSAGE)
|
||||
status = discord.Status.online
|
||||
|
@ -16,9 +13,4 @@ if __name__ == "__main__":
|
|||
intents.members = True
|
||||
|
||||
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)
|
||||
|
|
|
@ -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 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.",
|
||||
"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 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.",
|
||||
"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 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.",
|
||||
"inspire": "Generate quotes via [InspiroBot](https://inspirobot.me/).",
|
||||
"inspire": "Genereer quotes via [InspiroBot](https://inspirobot.me/).",
|
||||
"inventory": "Bekijk de items in jouw inventory.",
|
||||
"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 matches": "Bekijk de wedstrijden die gespeeld worden op [Week]. Default naar de huidige speeldag.",
|
||||
"jpl table": "De huidige stand van het klassement.",
|
||||
"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.",
|
||||
"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 all": "Laadt alle cogs in.",
|
||||
"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.
|
||||
"""
|
||||
fields = list(fields)
|
||||
|
||||
# If there's only one field, the user isn't required to use quotes
|
||||
if meme.fields == 1:
|
||||
fields = [" ".join(fields)]
|
||||
|
||||
fields = _applyMeme(meme, fields)
|
||||
fields = _apply_meme(meme, fields)
|
||||
|
||||
# List of fields to send to the API
|
||||
boxes = [{"text": ""}, {"text": ""}, {"text": ""}, {"text": ""}]
|
||||
|
@ -33,19 +35,22 @@ def generate(meme: Meme, fields):
|
|||
return {"success": False, "message": "Er is een fout opgetreden."}
|
||||
|
||||
# Post meme
|
||||
reply = _postMeme(meme, boxes)
|
||||
reply = _post_meme(meme, boxes)
|
||||
|
||||
# Adding a message parameter makes the code in the cog a lot cleaner
|
||||
if not reply["success"]:
|
||||
reply["success"] = False
|
||||
reply["message"] = "Error! Controleer of je de juiste syntax hebt gebruikt. Gebruik het commando " \
|
||||
"\"memes\" voor een lijst aan geaccepteerde meme-namen."
|
||||
else:
|
||||
reply["message"] = reply["data"]["url"]
|
||||
|
||||
reply["success"] = False
|
||||
|
||||
return reply
|
||||
|
||||
|
||||
def _postMeme(meme: Meme, boxes):
|
||||
def _post_meme(meme: Meme, boxes):
|
||||
"""
|
||||
Performs API request to generate the meme
|
||||
"""
|
||||
|
@ -65,7 +70,7 @@ def _postMeme(meme: Meme, boxes):
|
|||
return memeReply
|
||||
|
||||
|
||||
def _applyMeme(meme: Meme, fields):
|
||||
def _apply_meme(meme: Meme, fields):
|
||||
"""
|
||||
Some memes are in a special format that only requires
|
||||
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.
|
||||
"""
|
||||
memeDict = {
|
||||
102156234: _mockingSpongebob,
|
||||
91538330: _xXEverywhere,
|
||||
252600902: _alwaysHasBeen
|
||||
102156234: mocking_spongebob,
|
||||
91538330: _x_x_everywhere,
|
||||
252600902: _always_has_been,
|
||||
167754325: _math_is_math,
|
||||
206493414: _i_used_the_x_to_destroy_the_x
|
||||
}
|
||||
|
||||
# Meme needs no special treatment
|
||||
|
@ -86,17 +93,29 @@ def _applyMeme(meme: Meme, fields):
|
|||
return memeDict[meme.meme_id](fields)
|
||||
|
||||
|
||||
def _mockingSpongebob(fields):
|
||||
def mocking_spongebob(fields):
|
||||
return list(map(mock, fields))
|
||||
|
||||
|
||||
def _xXEverywhere(fields):
|
||||
def _x_x_everywhere(fields):
|
||||
word = fields[0]
|
||||
|
||||
return ["{}".format(word), "{} everywhere".format(word)]
|
||||
|
||||
|
||||
def _alwaysHasBeen(fields):
|
||||
def _always_has_been(fields):
|
||||
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
|
||||
|
||||
from discord import Interaction
|
||||
from discord.ext.commands import Context
|
||||
from dislash import SlashInteraction
|
||||
|
||||
|
||||
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}"
|
||||
|
||||
|
||||
def format_slash_command_usage(interaction: SlashInteraction) -> str:
|
||||
def format_slash_command_usage(interaction: Interaction) -> str:
|
||||
# Create a string with the options used
|
||||
# TODO look into the format used by the lib because it's terrible
|
||||
options = " ".join(list(map(
|
||||
lambda option: f"{option.name}: \"{option.value}\"",
|
||||
interaction.data.options.values()
|
||||
lambda o: f"{o['name']}: \"{o['value']}\"",
|
||||
interaction.data.get("options", [])
|
||||
)))
|
||||
|
||||
command = f"{interaction.slash_command.name} {options or ''}"
|
||||
return f"{interaction.author.display_name} in {_format_error_location(interaction)}: /{command}"
|
||||
command = f"{interaction.data['name']} {options or ''}"
|
||||
return f"{interaction.user.display_name} in {_format_error_location(interaction)}: /{command}"
|
||||
|
||||
|
||||
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
|
||||
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-binary==2.8.5
|
||||
python-dateutil==2.6.1
|
||||
|
@ -13,12 +13,7 @@ tabulate==0.8.7
|
|||
yarl==1.4.2
|
||||
feedparser==6.0.2
|
||||
googletrans==4.0.0rc1
|
||||
quart==0.15.1
|
||||
Quart-CORS==0.5.0
|
||||
attrs~=21.2.0
|
||||
dacite~=1.6.0
|
||||
pytest==6.2.4
|
||||
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
|
||||
|
||||
|
||||
load_dotenv()
|
||||
load_dotenv(verbose=True)
|
||||
|
||||
|
||||
def _to_bool(value: str) -> bool:
|
||||
|
@ -31,7 +31,6 @@ DB_NAME = os.getenv("DBNAME", "")
|
|||
|
||||
# Discord-related
|
||||
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
|
||||
STATUS_MESSAGE = os.getenv("STATUSMESSAGE", "with your Didier Dinks.")
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
from data.snipe import Snipe
|
||||
from discord.ext import commands, ipc
|
||||
from dislash import InteractionClient
|
||||
from discord.ext import commands
|
||||
import os
|
||||
from settings import HOST_IPC, SLASH_TEST_GUILDS
|
||||
from startup.init_files import check_all
|
||||
from typing import Dict
|
||||
|
||||
|
@ -11,30 +9,18 @@ class Didier(commands.Bot):
|
|||
"""
|
||||
Main Bot class for Didier
|
||||
"""
|
||||
# Reference to interactions client
|
||||
interactions: InteractionClient
|
||||
|
||||
# Dict to store the most recent Snipe info per channel
|
||||
snipe: Dict[int, Snipe] = {}
|
||||
|
||||
def __init__(self, *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
|
||||
self._preload = ("ipc", "utils", "failedchecks", "events",)
|
||||
self._preload = ("utils", "failedchecks", "events",)
|
||||
|
||||
# Remove default help command
|
||||
self.remove_command("help")
|
||||
|
||||
# Create interactions client
|
||||
self.interactions = InteractionClient(self, test_guilds=SLASH_TEST_GUILDS)
|
||||
|
||||
# Load all extensions
|
||||
self.init_extensions()
|
||||
|
||||
|
@ -65,9 +51,3 @@ class Didier(commands.Bot):
|
|||
# Subdirectory
|
||||
# Also walrus operator hype
|
||||
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