diff --git a/.gitignore b/.gitignore index 02e269f..9b3c80f 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,3 @@ files/ufora_notifications.json __pycache__ .env /venv/ -.pytest_cache \ No newline at end of file diff --git a/backend/server.py b/backend/server.py new file mode 100644 index 0000000..3450233 --- /dev/null +++ b/backend/server.py @@ -0,0 +1,82 @@ +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/") +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() diff --git a/cogs/events.py b/cogs/events.py index 6696d4c..70809b1 100644 --- a/cogs/events.py +++ b/cogs/events.py @@ -1,10 +1,11 @@ -from discord import Interaction +from dislash import SlashInteraction 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 @@ -85,7 +86,6 @@ class Events(commands.Cog): Logs commands in your terminal. :param ctx: Discord Context """ - print("a") print(stringFormatters.format_command_usage(ctx)) command_stats.invoked(command_stats.InvocationType.TextCommand) @@ -123,27 +123,24 @@ class Events(commands.Cog): await self.sendErrorEmbed(err, "Command", usage) @commands.Cog.listener() - async def on_interaction(self, interaction: Interaction): + async def on_slash_command(self, interaction: SlashInteraction): """ 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_application_command_error(self, ctx: discord.ApplicationContext, err): + async def on_slash_command_error(self, interaction, err): # Debugging Didier shouldn't spam the error logs if self.client.user.id != int(constants.didierId): raise err - if isinstance(err, commands.CheckFailure): - return await ctx.respond("Je hebt geen toegang tot dit commando.", ephemeral=True) + if isinstance(err, InteractionCheckFailure): + return await interaction.reply("Je hebt geen toegang tot dit commando.", ephemeral=True) - usage = stringFormatters.format_slash_command_usage(ctx.interaction) + usage = stringFormatters.format_slash_command_usage(interaction) await self.sendErrorEmbed(err, "Slash Command", usage) @commands.Cog.listener() diff --git a/cogs/fun.py b/cogs/fun.py index affd2c9..f797803 100644 --- a/cogs/fun.py +++ b/cogs/fun.py @@ -1,5 +1,5 @@ from data.embeds.xkcd import XKCDEmbed -from data.menus import paginated_leaderboard +from data.menus import paginatedLeaderboard from decorators import help import discord from discord.ext import commands @@ -124,9 +124,8 @@ class Fun(commands.Cog): memeList = [": ".join([stringFormatters.title_case(meme[1]), str(meme[2])]) for meme in sorted(memeList, key=lambda x: x[1])] - pages = paginated_leaderboard.Pages( - source=paginated_leaderboard.Source(memeList, "Memes", discord.Colour.blue()), - clear_reactions_after=True) + pages = paginatedLeaderboard.Pages(source=paginatedLeaderboard.Source(memeList, "Memes", discord.Colour.blue()), + clear_reactions_after=True) await pages.start(ctx) @commands.command(name="Pjoke") diff --git a/cogs/ipc.py b/cogs/ipc.py new file mode 100644 index 0000000..1e3c394 --- /dev/null +++ b/cogs/ipc.py @@ -0,0 +1,26 @@ +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)) diff --git a/cogs/leaderboards.py b/cogs/leaderboards.py index 6eb9edf..724bf54 100644 --- a/cogs/leaderboards.py +++ b/cogs/leaderboards.py @@ -1,4 +1,4 @@ -from data.menus import paginated_leaderboard +from data.menus import paginatedLeaderboard from decorators import help import discord from discord.ext import commands @@ -45,36 +45,35 @@ class Leaderboards(commands.Cog): user[1] += platDinks[str(user[0])] * Numbers.q.value entries[i] = user - data = [] + 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.empty_leaderboard(ctx, "Dinks Leaderboard", - "Er zijn nog geen personen met Didier Dinks.") + return await self.emptyLeaderboard(ctx, "Dinks Leaderboard", "Er zijn nog geen personen met Didier Dinks.") elif float(user[1]) + float(user[3]) > 0.0: - total_dinks = math.floor(float(user[1]) + float(user[3])) - data.append((user[0], total_dinks,)) - lb = paginated_leaderboard.Leaderboard( - ctx=ctx, title="Dinks Leaderboard", data=data, fetch_names=True - ) + # Get the username in this guild + name = self.utilsCog.getDisplayName(ctx, user[0]) - await lb.send(ctx) + 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) async def corona(self, ctx): - result = requests.get("https://disease.sh/v3/covid-19/countries").json() + 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: - data = [] - for country in result: - data.append((country["country"], f"{country['cases']:,}",)) + if land["country"] == "Belgium": + board.append("**{} ({:,})**".format(land["country"], land["cases"])) + else: + board.append("{} ({:,})".format(land["country"], land["cases"])) - lb = paginated_leaderboard.Leaderboard( - ctx=ctx, title="Corona Leaderboard", data=data, highlight="Belgium", - colour=discord.Colour.red() - ) - - await lb.send(ctx) + await self.startPaginated(ctx, board, "Corona Leaderboard", discord.Colour.red()) @leaderboard.command(name="Bitcoin", aliases=["Bc"], hidden=True) async def bitcoin(self, ctx): @@ -83,8 +82,7 @@ class Leaderboards(commands.Cog): 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.empty_leaderboard(ctx, "Bitcoin Leaderboard", - "Er zijn nog geen personen met Bitcoins.") + 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 @@ -103,8 +101,7 @@ class Leaderboards(commands.Cog): 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.empty_leaderboard(ctx, "Rob Leaderboard", - "Er heeft nog niemand Didier Dinks gestolen.") + 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 @@ -122,7 +119,7 @@ class Leaderboards(commands.Cog): 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.empty_leaderboard(ctx, "Poke Leaderboard", "Er is nog niemand getikt.") + return await self.emptyLeaderboard(ctx, "Poke Leaderboard", "Er is nog niemand getikt.") elif int(user[1]) == 0: break @@ -180,7 +177,7 @@ class Leaderboards(commands.Cog): 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.empty_leaderboard(ctx, "Muttn Leaderboard", "Der zittn nog geen muttns in de server.") + return await self.emptyLeaderboard(ctx, "Muttn Leaderboard", "Der zittn nog geen muttns in de server.") if float(user[1]) == 0: break @@ -193,15 +190,14 @@ class Leaderboards(commands.Cog): await self.startPaginated(ctx, boardTop, "Muttn Leaderboard") async def callLeaderboard(self, name, ctx): - command = [command for command in self.leaderboard.commands if command.name.lower() == name.lower()][0] - await command(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 = paginated_leaderboard.Pages(source=paginated_leaderboard.Source(source, name, colour), - clear_reactions_after=True) + pages = paginatedLeaderboard.Pages(source=paginatedLeaderboard.Source(source, name, colour), + clear_reactions_after=True) await pages.start(ctx) - async def empty_leaderboard(self, ctx, name, message, colour=discord.Colour.blue()): + async def emptyLeaderboard(self, ctx, name, message, colour=discord.Colour.blue()): embed = discord.Embed(colour=colour) embed.set_author(name=name) embed.description = message diff --git a/cogs/oneliners.py b/cogs/oneliners.py index 358a822..20bc822 100644 --- a/cogs/oneliners.py +++ b/cogs/oneliners.py @@ -151,7 +151,7 @@ class Oneliners(commands.Cog): @commands.command(name="Inspire") @help.Category(Category.Other) async def inspire(self, ctx): - image = get("https://inspirobot.me/api?generate=true") + image = get("http://inspirobot.me/api?generate=true") if image.status_code == 200: await ctx.send(image.text) diff --git a/cogs/slash/db_slash.py b/cogs/slash/db_slash.py new file mode 100644 index 0000000..bb2ee89 --- /dev/null +++ b/cogs/slash/db_slash.py @@ -0,0 +1,88 @@ +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)) + pass diff --git a/cogs/slash/football_slash.py b/cogs/slash/football_slash.py index 0f75eee..d10ba31 100644 --- a/cogs/slash/football_slash.py +++ b/cogs/slash/football_slash.py @@ -23,7 +23,6 @@ class FootballSlash(commands.Cog): @_jpl_group.command(name="table", description="Huidige rangschikking") async def _jpl_table_slash(self, ctx: ApplicationContext): - await ctx.response.defer() await ctx.respond(get_table()) @_jpl_group.command(name="update", description="Update de code voor deze competitie (owner-only)", default_permission=False) diff --git a/cogs/slash/other_slash.py b/cogs/slash/other_slash.py deleted file mode 100644 index 39350c7..0000000 --- a/cogs/slash/other_slash.py +++ /dev/null @@ -1,23 +0,0 @@ -from discord.ext import commands -from discord.commands import slash_command, ApplicationContext -from requests import get - -from startup.didier import Didier - - -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.") - - -def setup(client: Didier): - client.add_cog(OtherSlash(client)) diff --git a/cogs/store.py b/cogs/store.py index d24a500..25ee5c1 100644 --- a/cogs/store.py +++ b/cogs/store.py @@ -1,4 +1,5 @@ from converters.numbers import Abbreviated +from data.menus import storePages from decorators import help import discord from discord.ext import commands @@ -21,12 +22,11 @@ class Store(commands.Cog): @commands.check(checks.allowedChannels) @help.Category(Category.Currency) async def store(self, ctx): - pass - # entries = store.getAllItems() - # 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) - async def store_buy(self, ctx, item, amount: Abbreviated = 1): + async def storeBuy(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 store_sell(self, ctx, itemid, amount: Abbreviated = 1): + async def storeSell(self, ctx, itemid, amount: Abbreviated = 1): if amount is None: return await self.sell(ctx, itemid, amount) diff --git a/cogs/train.py b/cogs/train.py index 5865bf8..10dcbb0 100644 --- a/cogs/train.py +++ b/cogs/train.py @@ -1,4 +1,4 @@ -from data.menus import paginated_leaderboard +from data.menus import paginatedLeaderboard from decorators import help import discord from discord.ext import commands, menus @@ -36,10 +36,10 @@ class Train(commands.Cog): await self.sendEmbed(ctx, embed) return - pages = paginated_leaderboard.Pages(source=TrainPagination(self.formatConnections(req["connection"]), - self.formatCity(departure), - self.formatCity(destination)), - clear_reactions_after=True) + 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): diff --git a/data/menus/paginatedLeaderboard.py b/data/menus/paginatedLeaderboard.py new file mode 100644 index 0000000..45b5e35 --- /dev/null +++ b/data/menus/paginatedLeaderboard.py @@ -0,0 +1,31 @@ +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) diff --git a/data/menus/paginated_leaderboard.py b/data/menus/paginated_leaderboard.py deleted file mode 100644 index dd9bec7..0000000 --- a/data/menus/paginated_leaderboard.py +++ /dev/null @@ -1,127 +0,0 @@ -from typing import Callable - -import discord -from discord import ApplicationContext -from discord.ext import menus, pages -from dataclasses import dataclass - -from discord.ext.commands import Context - -from functions.utils import get_display_name - - -@dataclass -class Leaderboard: - ctx: Context - title: str - data: list - highlight: str = None - format_f: Callable = None - per_page: int = 10 - colour: discord.Colour = discord.Colour.blue() - fetch_names: bool = False - - def __post_init__(self): - if self.format_f is None: - self.format_f = self._format - - 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(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} ({data[1]})" - - return s - - def _get_page_count(self) -> int: - """Get the amount of pages required to represent this data""" - count = len(self.data) // self.per_page - if len(self.data) % self.per_page != 0: - count += 1 - - return count - - 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 - - def create_pages(self) -> list[discord.Embed]: - # Amount of entries added to this page - added = 0 - page_list = [] - - description = "" - for i, v in enumerate(self.data): - s = self.format_f(i, v) - - if self._should_highlight(v[0]): - s = f"**{s}**" - - 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 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(), show_disabled=False, disable_on_timeout=True, timeout=30) - - async def respond(self, ctx: ApplicationContext, **kwargs) -> discord.Message: - paginator = self.create_paginator() - return await paginator.respond(ctx.interaction, **kwargs) - - async def send(self, ctx: Context, **kwargs) -> discord.Message: - paginator = self.create_paginator() - return await paginator.send(ctx, **kwargs) - - -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) diff --git a/data/menus/storePages.py b/data/menus/storePages.py new file mode 100644 index 0000000..f794554 --- /dev/null +++ b/data/menus/storePages.py @@ -0,0 +1,28 @@ +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) diff --git a/didier.py b/didier.py index 461e9a2..4530c68 100644 --- a/didier.py +++ b/didier.py @@ -1,9 +1,12 @@ 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 @@ -13,4 +16,9 @@ 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) diff --git a/files/help.json b/files/help.json index f14e6f1..a522c9b 100644 --- a/files/help.json +++ b/files/help.json @@ -52,7 +52,7 @@ "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": "Genereer quotes via [InspiroBot](https://inspirobot.me/).", + "inspire": "Generate 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.", "jpl": "Informatie over de Jupiler Pro League.", diff --git a/functions/stringFormatters.py b/functions/stringFormatters.py index 37d67a0..f34e242 100644 --- a/functions/stringFormatters.py +++ b/functions/stringFormatters.py @@ -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,15 +42,16 @@ 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: Interaction) -> str: +def format_slash_command_usage(interaction: SlashInteraction) -> 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 o: f"{o['name']}: \"{o['value']}\"", - interaction.data.get("options", []) + lambda option: f"{option.name}: \"{option.value}\"", + interaction.data.options.values() ))) - command = f"{interaction.data['name']} {options or ''}" - return f"{interaction.user.display_name} in {_format_error_location(interaction)}: /{command}" + command = f"{interaction.slash_command.name} {options or ''}" + return f"{interaction.author.display_name} in {_format_error_location(interaction)}: /{command}" def get_edu_year(index: int) -> str: diff --git a/functions/utils.py b/functions/utils.py deleted file mode 100644 index b1a692a..0000000 --- a/functions/utils.py +++ /dev/null @@ -1,33 +0,0 @@ -from typing import Union - -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 diff --git a/requirements.txt b/requirements.txt index b19b0c7..e1c003b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,9 @@ 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,6 +16,8 @@ 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 diff --git a/settings.py b/settings.py index dcd8067..b2fed8e 100644 --- a/settings.py +++ b/settings.py @@ -4,7 +4,7 @@ from dotenv import load_dotenv import os -load_dotenv(verbose=True) +load_dotenv() def _to_bool(value: str) -> bool: @@ -31,6 +31,7 @@ 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.") diff --git a/startup/didier.py b/startup/didier.py index 4dc2615..d442e98 100644 --- a/startup/didier.py +++ b/startup/didier.py @@ -1,6 +1,8 @@ from data.snipe import Snipe -from discord.ext import commands +from discord.ext import commands, ipc +from dislash import InteractionClient import os +from settings import HOST_IPC from startup.init_files import check_all from typing import Dict @@ -9,14 +11,23 @@ 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 = ("utils", "failedchecks", "events",) + self._preload = ("ipc", "utils", "failedchecks", "events",) # Remove default help command self.remove_command("help") @@ -51,3 +62,9 @@ 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)