diff --git a/cogs/ERLC.py b/cogs/ERLC.py index 74db74e9..29f91684 100644 --- a/cogs/ERLC.py +++ b/cogs/ERLC.py @@ -868,7 +868,7 @@ async def operate_and_reload_serverinfo( queue: int = await self.bot.prc_api.get_server_queue( guild_id, minimal=True ) # this only returns the count - client = self.bot.roblox + client = roblox.Client() embed1 = discord.Embed(title=f"{status.name}", color=BLANK_COLOR) embed1.set_author(name=ctx.guild.name, icon_url=ctx.guild.icon) @@ -881,14 +881,10 @@ async def operate_and_reload_serverinfo( ), inline=False, ) - try: - owner_name = (await client.get_user(status.owner_id)).name - except: - owner_name = "Unknown" embed1.add_field( name="Server Ownership", value=( - f"> **Owner:** [{owner_name}](https://roblox.com/users/{status.owner_id}/profile)\n" + f"> **Owner:** [{(await client.get_user(status.owner_id)).name}](https://roblox.com/users/{status.owner_id}/profile)\n" f"> **Co-Owners:** {f', '.join([f'[{user.name}](https://roblox.com/users/{user.id}/profile)' for user in await client.get_users(status.co_owner_ids, expand=False)])}" ), inline=False, diff --git a/cogs/Sessions.py b/cogs/Sessions.py deleted file mode 100644 index 76c1b460..00000000 --- a/cogs/Sessions.py +++ /dev/null @@ -1,397 +0,0 @@ -import discord -from discord.ext import commands -from discord import app_commands -from erm import Bot, is_admin, require_settings, is_management -from utils.constants import CUSTOM_IDS_FOR_SESSIONS, SESSION_VIEW_TYPES -import discord.http -import json -from menus import CustomDropdown -from ui.CustomModals import CustomModalButton -from ui.Sessions import SessionsEmbedCreationView -from ui.Selects import SimpleTextChannelSelect -import utils.prc_api as prc_api -import matplotlib.pyplot as plt -import matplotlib -matplotlib.use("Agg") -import io, asyncio -import datetime - -def is_erlc_server_linked(): - async def predicate(ctx: commands.Context): - if ctx.guild is None: - return False - guild_id = ctx.guild.id - - try: - await ctx.bot.prc_api.get_server_status(guild_id) - except prc_api.ResponseFailure as exc: - error = prc_api.ServerLinkNotFound(platform="erlc") - try: - error.code = exc.json_data.get("code") or exc.status_code - except json.JSONDecodeError: - pass - raise error - - return True - - return commands.check(predicate) - -class Sessions(commands.Cog): - def __init__(self, bot: Bot): - self.bot = bot - plt.style.use("dark_background") - self.fig, self.ax = plt.subplots(figsize=(8, 6)) - - @commands.Cog.listener() - async def on_interaction(self, interaction: discord.Interaction): - if interaction.type != discord.InteractionType.component or not interaction.message: - return - id = interaction.data.get("custom_id") - if not id.endswith(f":{interaction.guild.id}"): - return - id = id.removesuffix(f":{interaction.guild.id}") - if not id in CUSTOM_IDS_FOR_SESSIONS: - return - - guild = interaction.guild - settings = await self.bot.settings.find(guild.id) - if not settings: return - view = discord.ui.View.from_message(interaction.message, timeout=None) - session = await self.bot.sessions.find(guild.id) - if not session: - return - if id == "vote_button": - if interaction.user.id in session["voted_users"]: - action = "decrement" - else: - action = "increment" - session["votes"] += 1 if action == "increment" else -1 - if action == "decrement": - session["voted_users"].remove(interaction.user.id) - else: - session["voted_users"].append(interaction.user.id) - - if settings["sessions"].get("dynamic_button"): - item = None - - for c in view.walk_children(): - if isinstance(c, discord.ui.Button) and c.custom_id == f"vote_button:{guild.id}": - item = c - break - - if item is None: - return - item.label = f"{session["votes"]}/{session["required_votes"]}" - await interaction.response.edit_message(view=view) - else: - await interaction.response.send_message("Successfully counted your vote for the session." if action == "increment" else "Successfully removed your vote from the session.") - await self.bot.sessions.update(session) - return - elif id == "view_votes_button": - print("e") - cont = discord.ui.Container( - discord.ui.TextDisplay( - "### Voters\n" - f"{"".join([f"- <@{str(user)}>\n" for user in session["voted_users"]]) or "> No people have voted for the session."}" - ) - ) - return await interaction.response.send_message(view=discord.ui.LayoutView().add_item(cont), ephemeral=True) - - @commands.hybrid_group(name = "session", description="Session-related commands") - async def session(self, ctx: commands.Context): - if not ctx.invoked_subcommand: - return await ctx.reply(embed=discord.Embed(title="Invalid Subcommand", description="No valid subcommand was invoked.")) - - @session.command(name = "vote", description="Create a session vote") - @require_settings(["sessions"]) - @is_admin() - @app_commands.describe(required_votes="The votes required for the session to start") - async def _vote(self, ctx: commands.Context, required_votes: int | None=None): - settings = await self.bot.settings.find(ctx.guild.id) - if not settings: - return - if await self.bot.sessions.find(ctx.guild.id): - return await ctx.reply(embed=discord.Embed( - title = "Current Session", - description="There is already an active session." - )) - - session_data = { - "_id": ctx.guild.id, - "user": ctx.author.id, - "voted_users": [], - "started": False, - "votes": 0, - "required_votes": required_votes or settings["sessions"]["required_votes_default"] or 5, - "analytics": { - "max_players": 0, - "player_counts": [] - } - } - - d = settings["sessions"]["vote"].replace( - "{user}", - ctx.author.mention - ).replace( - "{vote_button_name}", - settings["sessions"].get("vote_button_label", "vote") if not settings["sessions"].get("dynamic_button") else f"0/{session_data["required_votes"]}" - ).replace( - "{required_members}", - str(required_votes or settings["sessions"]["required_votes_default"] or 5) - ) - j = json.loads(d) - if settings["sessions"].get("dynamic_button"): - j["components"][0]["components"][0]["label"] = f"0/{session_data["required_votes"]}" - - msg = await self.bot.http.send_message(settings["sessions"]["channel_id"], params=discord.http.MultipartParameters(payload = j, multipart=None, files=None)) - session_data["vote_message"] = msg["id"] - await self.bot.sessions.insert(session_data) - return await (ctx.reply if not ctx.interaction else ctx.interaction.followup.send)(embed=discord.Embed(title = f"{self.bot.emoji_controller.get_emoji("success")} Successfully posted session vote message", description=f"You can find it at <#{settings["sessions"]["channel_id"]}>", colour=discord.Colour.green()), ephemeral=True) - - @session.command(name = "start", description="Start a session") - @require_settings(["sessions"]) - @is_admin() - async def _start(self, ctx: commands.Context): - settings = await self.bot.settings.find(ctx.guild.id) - if not settings: - return - session = await self.bot.sessions.find(ctx.guild.id) - if not session: - return await ctx.reply(embed=discord.Embed( - title = "No Session", - description="There is no active session." - )) - try: - info = await self.bot.prc_api.get_server_status(ctx.guild.id) - except: - info = None - if "{erlc.players}" in settings["sessions"]["start"]: session["dynamic"] = True - d = settings["sessions"]["start"].replace( - "{user}", - ctx.author.mention - ).replace( - "{user_mentions}", - f"{" | ".join([f"<@{user}>" for user in session["voted_users"]])}" - ).replace( - "{erlc.name}", - info.name if info else "{erlc.name}" - ).replace( - "{erlc.code}", - f"{info.join_key}" if info else "{erlc.code}" - ).replace( - "{erlc.players}", - str(info.current_players if info else "{erlc.players}") - ) - session["user"] = ctx.author.mention - - j = json.loads(d) - channel = await ctx.guild.fetch_channel(settings["sessions"]["channel_id"]) - msg = await channel.fetch_message(session["vote_message"]) - view = discord.ui.View.from_message(msg) - item = None - - for c in view.walk_children(): - if isinstance(c, discord.ui.Button) and c.custom_id == f"vote_button:{ctx.guild.id}": - item = c - break - if item == None: - pass - else: - item.disabled = True - await msg.edit(view=view) - s = await self.bot.http.send_message(settings["sessions"]["channel_id"], params=discord.http.MultipartParameters(payload = j, multipart=None, files=None)) - session["message"], session["channel"] = s["id"], settings["sessions"]["channel_id"] - await self.bot.sessions.update(session) - return await (ctx.reply if not ctx.interaction else ctx.interaction.followup.send)(embed=discord.Embed(title = f"{self.bot.emoji_controller.get_emoji("success")} Successfully posted session start message", description=f"You can find it at <#{settings["sessions"]["channel_id"]}>", colour=discord.Colour.green()), ephemeral=True) - - @session.command(name = "end", description="End a session") - @require_settings(["sessions"]) - @is_admin() - async def _end(self, ctx: commands.Context): - settings = await self.bot.settings.find(ctx.guild.id) - if not settings: - return - session = await self.bot.sessions.find(ctx.guild.id) - if not session: - return await ctx.reply(embed=discord.Embed( - title = "No Session", - description="There is no active session." - )) - try: - info = await self.bot.prc_api.get_server_status(ctx.guild.id) - except: info = None - d = settings["sessions"]["shutdown"].replace( - "{user}", - ctx.author.mention - ).replace( - "{erlc.name}", - info.name if info else "{erlc.name}" - ).replace( - "{erlc.code}", - info.join_key if info else "{erlc.code}" - ).replace( - "{erlc.max_players}", - str(session.get("analytics", {}).get("max_players", 0)) - ) - j = json.loads(d) - await self.bot.http.send_message(settings["sessions"]["channel_id"], params=discord.http.MultipartParameters(payload = j, multipart=None, files=None)) - await self.bot.sessions.delete(session["_id"]) - return await (ctx.reply if not ctx.interaction else ctx.interaction.followup.send)(embed=discord.Embed(title = f"{self.bot.emoji_controller.get_emoji("success")} Successfully posted session end message", description=f"You can find it at <#{settings["sessions"]["channel_id"]}>", colour=discord.Colour.green()), ephemeral=True) - def generate_graph(self, session): - """ - Generates player graph. - Very blocking so run in an executor - """ - self.ax.clear() - self.ax.plot(session["analytics"]["player_counts"], label="Current Players") - self.ax.set_xticks([]) - self.ax.set_title("Player Graph") - self.ax.set_ylabel("Player Count") - self.ax.legend(loc='upper center', ncol=8, frameon=True) - self.ax.margins(x=0) - self.fig.tight_layout() - - buf = io.BytesIO() - self.fig.savefig(buf, format="png", bbox_inches="tight", dpi=100, facecolor="black") - buf.seek(0) - return buf - @session.command(name = "info", description="View analytics about your session") - @require_settings(["sessions"]) - @is_erlc_server_linked() - async def _info(self, ctx: commands.Context): - session = await self.bot.sessions.find(ctx.guild.id) - if not session or not session.get("message"): - return await ctx.reply(embed=discord.Embed( - title = "No Session", - description="There is no active session." - )) - info = await self.bot.prc_api.get_server_status(ctx.guild.id) - cont = discord.ui.Container(discord.ui.TextDisplay( - "### Session Status\n" - "Below are some analytics regarding your current session. ERM collects data such as your player counts and max player counts and these are deleted when the session is over." - )) - graph = await asyncio.get_event_loop().run_in_executor(None, self.generate_graph, session) - cont.add_item(discord.ui.Separator()) - cont.add_item(discord.ui.TextDisplay( - "### Player Analytics\n" - f"> **Current Amount of Players**: {info.current_players}\n" - f"> **Current Amount of Modcalls**: {len(await self.bot.prc_api.get_mod_calls(ctx.guild.id))}\n" - f"> **Highest Player Count**: {session["analytics"]["max_players"]}" - )) - cont.add_item(discord.ui.Separator()) - file = discord.File(graph, filename="graph.png") - cont.add_item(discord.ui.MediaGallery(discord.MediaGalleryItem(discord.UnfurledMediaItem("attachment://graph.png")))) - cont.add_item(discord.ui.Separator()) - cont.add_item(discord.ui.ActionRow(discord.ui.Button(url=f"https://erlc.gg/join/{info.join_key}", label="Join Server"))) - return await ctx.reply(view=discord.ui.LayoutView().add_item(cont), files=[file]) - - - @session.command(name = "config", description = "Manage the session config") - @is_management() - @require_settings() - async def _config(self, ctx: commands.Context): - settings = await self.bot.settings.find(ctx.guild.id) - if not settings: - return - if not settings.get("sessions"): - settings["sessions"] = {} - sel = CustomDropdown( - ctx.author.id, - [ - discord.SelectOption(label = "Channel", description="Select the channel for sessions to be sent to.", value="channel"), - discord.SelectOption(label = "Vote Message", description="Edit the session vote message", value="vote"), - discord.SelectOption(label = "Start Message", description="Edit the start message", value = "start"), - discord.SelectOption(label = "End Message", description="Edit the session end message", value = "shutdown"), - discord.SelectOption(label = "Other Options", description="Edit other options, such as the dynamic button.", value = "other"), - discord.SelectOption(label = "Finish", description="Finish editing these options.", value = "done") - ] - ) - msg: discord.Message = None - while True: - cont = discord.ui.Container( - discord.ui.TextDisplay( - "### Configure Sessions\n" - "Please select the item in the dropdown below to configure sessions." - ), - discord.ui.Separator(), - discord.ui.ActionRow(sel) - ) - - if not msg: - msg = await ctx.reply(view = (view := discord.ui.LayoutView().add_item(cont))) - else: - await msg.edit(view = (view := discord.ui.LayoutView().add_item(cont))) - await view.wait() - match sel.values[0]: - case "channel": - ch = SimpleTextChannelSelect(default_values=[discord.SelectDefaultValue(id=settings["sessions"].get("channel_id", 0), type=discord.SelectDefaultValueType.channel)]) - cont = discord.ui.Container( - discord.ui.TextDisplay( - "### Set Session Channel\n" - "Please select the channel for session messages to be sent to" - ), - discord.ui.Separator(), - discord.ui.ActionRow(ch) - ) - await msg.edit(view = (view := discord.ui.LayoutView().add_item(cont))) - await view.wait() - print("e") - settings['sessions']["channel_id"] = ch.values[0].id - case "other": - modal = CustomModalButton( - ctx.author.id, - "Set Other Options", - "Set Other Options", - [ - ( - "vote_button_name", - discord.ui.Label( - text = "Vote Button Label", - description="If you are not using the dynamic button, set a vote label name.", - component=discord.ui.TextInput( - default = settings["sessions"].get("vote_button_label", "vote") - ) - ) - ), - ( - "default_required_votes", - discord.ui.Label( - text = "Default Required Votes", - description="If a user does not specify the amount of votes, this will be used instead.", - component=discord.ui.TextInput(default = settings["sessions"].get("required_votes_default", 5)) - ) - ), - ( - "dynamic_button", - discord.ui.Label( - text = "Dynamic Button", - description="Do you wish to enable the dynamic button?", - component=discord.ui.Checkbox(default = settings["sessions"].get("dynamic_button", False)) - ) - ) - ] - ) - cont = discord.ui.Container( - discord.ui.TextDisplay( - "### Other Options\n" - "Please press the button below to continue!" - ), - discord.ui.Separator(), - discord.ui.ActionRow(modal) - ) - await msg.edit(view = (view := discord.ui.LayoutView().add_item(cont))) - await view.wait() - settings["sessions"]["vote_button_name"] = modal.values[0] - settings["sessions"]["required_votes_default"] = modal.values[1] - settings["sessions"]["dynamic_button"] = bool(modal.values[2]) - case "done": - await self.bot.settings.update(settings) - return await msg.edit(view=discord.ui.LayoutView().add_item(discord.ui.TextDisplay("### Success\nYour settings were successfully saved!"))) - case val if sel.values[0] in SESSION_VIEW_TYPES: - await self.bot.settings.update(settings) - await msg.edit(view=(view:=SessionsEmbedCreationView(self.bot, type=val))) - await view.wait() - settings = await self.bot.settings.find(ctx.guild.id) - -async def setup(bot: Bot): - await bot.add_cog(Sessions(bot)) \ No newline at end of file diff --git a/erm.py b/erm.py index 171341bc..88a72769 100644 --- a/erm.py +++ b/erm.py @@ -192,8 +192,7 @@ async def setup_hook(self) -> None: self.server_keys = ServerKeys(self.db, "server_keys") self.maple_county = self.mongo[f"{f"{dbname}_" if dbname != "erm" else ""}MapleCounty"] self.mc_keys = MapleKeys(self.maple_county, "Auth") - self.sessions = Document(self.db, "sessions") - + self.staff_connections = StaffConnections(self.db, "staff_connections") self.ics = IntegrationCommandStorage(self.db, "logged_command_data") self.actions = Actions(self.db, "actions") diff --git a/tasks/check_sessions.py b/tasks/check_sessions.py deleted file mode 100644 index 51cbc408..00000000 --- a/tasks/check_sessions.py +++ /dev/null @@ -1,41 +0,0 @@ -from discord.ext import tasks -import discord -import logging -from erm import Bot -import discord.http -import json -@tasks.loop(minutes=5, reconnect=True) -async def check_sessions(bot: Bot): - print("Ran!") - async for session in bot.sessions.db.find({"dynamic": True}): - print(f"Parsing session for guild {session["_id"]}") - try: - guild = session["_id"] - g = await bot.fetch_guild(guild) - settings = await bot.settings.find(guild) - try: - info = await bot.prc_api.get_server_status(guild) - except: info = None - print(info) - d = settings["sessions"]["start"].replace( - "{user}", - session["user"] - ).replace( - "{erlc.name}", - info.name if info else "{erlc.name}" - ).replace( - "{erlc.code}", - f"{info.join_key}" if info else "{erlc.code}" - ).replace( - "{erlc.players}", - str(info.current_players) if info else "{erlc.players}" - ) - j = json.loads(d) - await bot.http.edit_message(settings["sessions"]["channel_id"], session["message"], params=discord.http.MultipartParameters(payload = j, multipart=None, files=None)) - if info: - if info.current_players > session["analytics"]["max_players"]: - session["analytics"]["max_players"] = info.current_players - session["analytics"]["player_counts"].append(info.current_players) - await bot.sessions.update(session) - except Exception as e: - logging.warning(f"error: {str(e)}") \ No newline at end of file diff --git a/tasks/iterate_prc_logs.py b/tasks/iterate_prc_logs.py index 076613de..7efd36b9 100644 --- a/tasks/iterate_prc_logs.py +++ b/tasks/iterate_prc_logs.py @@ -209,7 +209,6 @@ async def process_guild(bot, items, semaphore): except Exception as e: logging.warning(f"error processing guild: {e}") - await iterate_prc_logs(bot) diff --git a/ui/CustomModals.py b/ui/CustomModals.py deleted file mode 100644 index 7cc4fc34..00000000 --- a/ui/CustomModals.py +++ /dev/null @@ -1,103 +0,0 @@ -import discord, typing -from menus import CustomModal -from utils.utils import generalised_interaction_check_failure - -class CustomModalButton(discord.ui.Button): - def __init__( - self, - user_id, - title: str, - label: str, - options: typing.List[typing.Tuple[str | typing.Literal[str], discord.ui.TextInput | discord.ui.Label | discord.ui.TextDisplay]], - epher_args: typing.Optional[dict] = None, - ): - super().__init__(label=label or "Enter Strike Amount", style=discord.ButtonStyle.secondary) - self.value = None - self.user_id = user_id - self.modal: typing.Union[None, CustomModal] = None - self.title = title or self.label - self.label = label - self.options = options - self.epher_args = epher_args or {} - - # When the confirm button is pressed, set the inner value to `True` and - # stop the View from listening to more input. - # We also send the user an ephemeral message that we're confirming their choice. - async def callback(self, interaction: discord.Interaction): - if interaction.user.id != self.user_id: - await interaction.response.defer(ephemeral=True, thinking=True) - return await generalised_interaction_check_failure(interaction.followup) - - self.modal = CustomModal(self.label, self.options, self.epher_args) - await interaction.response.send_modal(self.modal) - await self.modal.wait() - - self.values = [] - for component in self.modal.children: - if isinstance(component, discord.ui.TextDisplay): continue - if isinstance(component, discord.ui.Label): - # Labels have a .component attribute - target = component.component - else: - # Other components are the target themselves - target = component - - # Skip if target doesn't have value/values - if hasattr(target, 'values'): - self.values.append(target.values) - elif hasattr(target, 'value'): - self.values.append(target.value) - self.parent.view.values = self.values - self.parent.view.stop() - return - -class CustomModalExecutorButton(discord.ui.Button): - def __init__( - self, - user_id, - title: str, - label: str, - options: typing.List[typing.Tuple[str | typing.Literal[str], discord.ui.TextInput | discord.ui.Label]], - func: typing.Callable, - epher_args: typing.Optional[dict] = None, - - ): - super().__init__(label=label or "Enter Strike Amount", style=discord.ButtonStyle.secondary) - self.value = None - self.user_id = user_id - self.modal: typing.Union[None, CustomModal] = None - self.title = title or self.label - self.label = label - self.options = options - self.func = func - self.epher_args = epher_args or {} - - # When the confirm button is pressed, set the inner value to `True` and - # stop the View from listening to more input. - # We also send the user an ephemeral message that we're confirming their choice. - async def callback(self, interaction: discord.Interaction): - if interaction.user.id != self.user_id: - await interaction.response.defer(ephemeral=True, thinking=True) - return await generalised_interaction_check_failure(interaction.followup) - - self.modal = CustomModal(self.label, self.options, self.epher_args) - await interaction.response.send_modal(self.modal) - await self.modal.wait() - - self.values = [] - for component in self.modal.children: - if isinstance(component, discord.ui.TextDisplay): continue - if isinstance(component, discord.ui.Label): - # Labels have a .component attribute - target = component.component - else: - # Other components are the target themselves - target = component - - # Skip if target doesn't have value/values - if hasattr(target, 'values'): - self.values.append(target.values) - elif hasattr(target, 'value'): - self.values.append(target.value) - await self.func(interaction, self) - \ No newline at end of file diff --git a/ui/Selects.py b/ui/Selects.py deleted file mode 100644 index 44aadb6a..00000000 --- a/ui/Selects.py +++ /dev/null @@ -1,8 +0,0 @@ -import discord -class SimpleTextChannelSelect(discord.ui.ChannelSelect): - def __init__(self, limit=1, **kwargs): - super().__init__(placeholder="Select Channels" if limit > 1 else "Select a Channel", max_values=limit, channel_types=[discord.ChannelType.text], **kwargs) - - async def callback(self, interaction: discord.Interaction): - await interaction.response.defer() - await self.parent.view.stop() \ No newline at end of file diff --git a/ui/Sessions.py b/ui/Sessions.py deleted file mode 100644 index d52f5c7a..00000000 --- a/ui/Sessions.py +++ /dev/null @@ -1,149 +0,0 @@ -import discord -from discord.ext import commands -import typing -from menus import CustomModal -from base64 import urlsafe_b64decode, b64encode -import json - - -class SessionsEmbedCreationView(discord.ui.LayoutView): - def __init__(self, bot: commands.Bot, type: typing.Literal['vote', 'start', 'shutdown'],): - super().__init__(timeout=None) - self.cont = discord.ui.Container() - self.type = type - self.bot = bot - match type: - case 'vote': - self.cont.add_item( - discord.ui.TextDisplay( - "### Create Session Vote Message\n" - "Please use Discohook to design your session vote message. When you are done, please press the button saying 'I Have Created My Message'. Please note that you may only have one message.\n" - "**Main Variables**\n" - "- Your vote button must be a button and must have the label set to `{vote_button}`. If it is not set to that, your vote button will not work.\n" - "- If you want a view votes button, you must have another button with the label set to `{view_votes_button}`, else, that will not work.\n" - "**Other Variables**\n" - "- {user}: The user initiating the session vote\n" - "- {vote_button_name}: The name of the vote button. If you select the dynamic button option (where the button's name changes on the amount of votes), this will say 'vote' by default.\n" - "- {required_members}: How many members are needed to start the vote\n" - ) - ) - case 'start': - self.cont.add_item(discord.ui.TextDisplay( - "### Create Session Start Message\n" - "Please use Discohook to create your session start message. When you are done, press the button saying 'I Have Created My Message'. Please note that you may only have one message.\n" - "**Main Variables and Notes**\n" - "- You will not be able to add any other components than __link buttons__. Regular buttons and anything else will be ignored.\n" - "- It is advised that if you create a join URL, you set the code part to {erlc.code}. It will automatically change to your join code.\n" - "**Other Variables**\n" - "- {user}: The user who started the session\n" - "- {user_mentions}: The mentions of the users who voted.\n" - "- {erlc}: A variable with references to your linked ERLC server\n" - " - {erlc.name}: The name of your ER:LC server\n" - " - {erlc.code}: The code to your ER:LC server\n" - " - {erlc.players}: The players in your ER:LC server. If this is present, your message will be edited every 5 minutes to reflect the current amount of members.\n" - )) - case 'shutdown': - self.cont.add_item(discord.ui.TextDisplay( - "### Create Session End Message\n" - "Please use Discohook to create your session end message. When you are done, press the button saying 'I Have Created My Message'. Please note that you may only have one message.\n" - "**Main Variables and Notes**\n" - "- Only link buttons are permitted.\n" - "**Other Variables**:\n" - "- {user}: The user responsible for ending this session\n" - "- {erlc}: A variable with references to your linked ERLC server \n" - " - {erlc.name}: The name of your ER:LC server\n" - " - {erlc.max_players}: The highest amount of players." - )) - self.cont.add_item(discord.ui.Separator()) - self.row = discord.ui.ActionRow(discord.ui.Button(url="https://discohook.app", label="Access Discohook")) - self.button = discord.ui.Button( - label = "I Have Created My Message", - style=discord.ButtonStyle.blurple - ) - self.button.callback = self.submit - self.row.add_item(self.button) - self.cont.add_item(self.row) - self.add_item(self.cont) - async def submit(self, interaction: discord.Interaction): - try: - settings = await self.bot.settings.find(interaction.guild.id) - if not settings.get('sessions'): - return - modal = CustomModal( - "Submit Message", - [ - ( - "url", - discord.ui.Label( - text="URL", - description="Please send your URL into this textbox.", - component=discord.ui.TextInput(max_length=3999) - ) - ) - ] - ) - await interaction.response.send_modal(modal) - await modal.wait() - val: str = modal.url.component.value - if not val.startswith("https://discohook.app"): - return await interaction.followup.send("This is not a valid discohook.app URL.", ephemeral=True) - try: - data_encoded = val.split("?data=")[1] - except: - return await interaction.followup.send("This discohook.app url does not have a message attached.") - print(data_encoded) - - data = urlsafe_b64decode(data_encoded + "==").decode() - try: - data = json.loads(data) - except: - return await interaction.followup.send("The data is not valid. Please try again.") - - message = data["messages"][0]["data"] - self.satisfied_conditions = False # Checks for buttons being correct. - if not message["components"] and self.type != "vote": - print(message["components"]) - self.satisfied_conditions = True - else: - for component in message["components"][0]["components"]: # - print(component) - if not component["type"] == 2: # button - return await interaction.followup.send("Your data has components that are not permitted.") - match self.type: - case 'vote': - if component["label"] == "{vote_button}": - if not settings["sessions"].get("dynamic_button", True): - component["label"] = settings["sessions"]["vote_button_label"] - if component["style"] == 5: continue - component["custom_id"] = f"vote_button:{interaction.guild.id}" - self.satisfied_conditions = True - continue - if component["label"] == "{view_votes_button}": - if component["style"] == 5: continue - component["label"] = "View Votes" - component["custom_id"] = f"view_votes_button:{interaction.guild.id}" - continue - if component["style"] == 5: - if component["custom_id"]: - del component["custom_id"] - case 'start': - if component["style"] == 5: - if component["custom_id"]: - del component["custom_id"] - self.satisfied_conditions = True - break - - case 'shutdown': - if component["style"] == 5: - self.satisfied_conditions = True - break - - if not self.satisfied_conditions: - return await interaction.followup.send("Your components are invalid for the specific type of embed you are making. This may be because you have regular buttons in the start and end styling (which only allow links) or you may have missing buttons in the vote area.") - - settings["sessions"][self.type] = json.dumps(message) - print(settings["sessions"]) - await self.bot.settings.update(settings) - self.stop() - except Exception as e: - print(str(e.with_traceback(None))) \ No newline at end of file diff --git a/utils/constants.py b/utils/constants.py index 4b83464d..c6b899c7 100644 --- a/utils/constants.py +++ b/utils/constants.py @@ -182,7 +182,4 @@ }, "remove_ingame_perms": False, "end_shift": False -} - -CUSTOM_IDS_FOR_SESSIONS = ["vote_button", "view_votes_button"] -SESSION_VIEW_TYPES = ["vote", "start", "shutdown"] \ No newline at end of file +} \ No newline at end of file diff --git a/utils/mongo.py b/utils/mongo.py index 755aec79..b168a837 100644 --- a/utils/mongo.py +++ b/utils/mongo.py @@ -21,7 +21,7 @@ def __init__(self, connection: AsyncDatabase, document_name): self.logger = logging.getLogger(__name__) # <-- Pointer Methods --> - async def update(self, dict) -> None: + async def update(self, dict): """ For simpler calls, points to self.update_by_id """ @@ -46,7 +46,7 @@ async def delete(self, id): await self.delete_by_id(id) # <-- Actual Methods --> - async def find_by_id(self, id) -> dict | None: + async def find_by_id(self, id): """ Returns the data found under `id` Params: @@ -149,7 +149,7 @@ async def increment(self, id, amount, field): """ await self.db.update_one({"_id": id}, {"$inc": {field: amount}}) - async def get_all(self) -> list[dict]: + async def get_all(self): """ Returns a list of all data in the document """ diff --git a/utils/prc_api.py b/utils/prc_api.py index 1f51d7ce..f93566eb 100644 --- a/utils/prc_api.py +++ b/utils/prc_api.py @@ -203,7 +203,7 @@ async def send_test_request(self, server_key: str) -> int | ServerStatus: ) ) - async def get_server_players(self, guild_id: int) -> list[Player]: + async def get_server_players(self, guild_id: int) -> list: status_code, response_json = await self._send_api_request( "GET", "/server/players", guild_id ) diff --git a/utils/utils.py b/utils/utils.py index 4ee1cc22..517c84c6 100644 --- a/utils/utils.py +++ b/utils/utils.py @@ -203,12 +203,12 @@ class GuildCheckFailure(commands.CheckFailure): pass -def require_settings(setting_lists: list[str]=[]): +def require_settings(): async def predicate(ctx: commands.Context): if ctx.guild is None: return True settings = await ctx.bot.settings.find_by_id(ctx.guild.id) - if not settings or not all(setting in settings for setting in setting_lists): + if not settings: raise GuildCheckFailure() else: return True @@ -218,7 +218,7 @@ async def predicate(ctx: commands.Context): async def update_ics(bot, ctx, channel, return_val: dict, ics_id: int): try: - status: ServerStatus|None = await bot.prc_api.get_server_status(ctx.guild.id) + status: ServerStatus = await bot.prc_api.get_server_status(ctx.guild.id) except prc_api.ResponseFailure: status = None if not isinstance(status, ServerStatus):