Compare commits

..

No commits in common. "4e0938035143da6efa59b9c1580c7816b3c02308" and "cb42243a65eaab0cfb21bff2133d84b253f98cfb" have entirely different histories.

View file

@ -1,10 +1,10 @@
import asyncio import asyncio
import aiohttp import aiohttp
import discord import discord
import requests
from discord import ui from discord import ui
from discord.ext import commands from discord.ext import commands
from redbot.core import Config, app_commands, commands from redbot.core import commands, app_commands, Config
class Pterodactyl(commands.Cog): class Pterodactyl(commands.Cog):
"""Pterodactyl allows you to manage your Pterodactyl Panel from Discord.""" """Pterodactyl allows you to manage your Pterodactyl Panel from Discord."""
@ -17,16 +17,8 @@ class Pterodactyl(commands.Cog):
api_key=None, api_key=None,
server_id=None, server_id=None,
startup_jar=None, startup_jar=None,
startup_arguments=None, startup_arguments=None
power_action_in_progress=False
) )
self.session: aiohttp.ClientSession = None
async def cog_load(self):
self.session = aiohttp.ClientSession()
async def cog_unload(self):
await self.session.close()
async def get_headers(self, guild: discord.Guild): async def get_headers(self, guild: discord.Guild):
"""Returns the headers used to access the Pterodactyl API.""" """Returns the headers used to access the Pterodactyl API."""
@ -52,17 +44,22 @@ class Pterodactyl(commands.Cog):
url += '/' + endpoint url += '/' + endpoint
return url return url
async def put(self, url: str, headers: dict, data: dict):
"""Sends an asyncio PUT request to the specified URL with the specified headers and data."""
async with aiohttp.ClientSession() as session:
async with session.put(url, headers=headers, json=data) as response:
return response
@app_commands.command(name="update", description="Updates the server.") @app_commands.command(name="update", description="Updates the server.")
@app_commands.guild_only() @app_commands.guild_only()
async def update(self, interaction: discord.Interaction): async def update(self, interaction: discord.Interaction):
"""Updates the server using the arguments provided in the server's configuration.""" """Updates the server using the arguments provided in the server's configuration."""
if await self.config.guild(interaction.guild).power_action_in_progress() is True:
await interaction.response.send_message(ephemeral=True, content="Power action already in progress!\nTry again later.")
return
session = self.session
await interaction.response.defer(ephemeral=True, thinking=True) await interaction.response.defer(ephemeral=True, thinking=True)
interaction_message = await interaction.original_response() interaction_message = await interaction.original_response()
if await self.config.guild(interaction.guild).startup_jar() is None: if await self.config.guild(interaction.guild).api_key() is None:
await interaction_message.edit(f"Something went wrong.\nError: `API Key not set.`", ephemeral=True)
raise LookupError("API Key not set.")
elif await self.config.guild(interaction.guild).startup_jar() is None:
await interaction_message.edit(f"Something went wrong.\nError: `Startup jar not set.`", ephemeral=True) await interaction_message.edit(f"Something went wrong.\nError: `Startup jar not set.`", ephemeral=True)
raise LookupError("Startup jar not set.") raise LookupError("Startup jar not set.")
elif await self.config.guild(interaction.guild).startup_arguments() is None: elif await self.config.guild(interaction.guild).startup_arguments() is None:
@ -71,15 +68,11 @@ class Pterodactyl(commands.Cog):
else: else:
startup_jar = await self.config.guild(interaction.guild).startup_jar() startup_jar = await self.config.guild(interaction.guild).startup_jar()
startup_commands = await self.config.guild(interaction.guild).startup_arguments() startup_commands = await self.config.guild(interaction.guild).startup_arguments()
try:
headers = await self.get_headers(interaction.guild) headers = await self.get_headers(interaction.guild)
except LookupError as e: response = requests.get(await self.get_url(interaction.guild, "resources"), headers=headers)
await interaction_message.edit(f"Something went wrong.\nError: `{e}`", ephemeral=True) response_dict = response.json()
return list_var = requests.get(await self.get_url(interaction.guild, "startup"), headers=headers)
async with session.get(await self.get_url(interaction.guild, "resources"), headers=headers) as response: list_var_response_dict = list_var.json()
response_dict = await response.json()
async with session.get(await self.get_url(interaction.guild, "startup"), headers=headers) as response:
list_var_response_dict = await response.json()
updater_startup_vars = [ updater_startup_vars = [
{ {
"key": "FLAGS", "key": "FLAGS",
@ -101,25 +94,24 @@ class Pterodactyl(commands.Cog):
} }
] ]
if response_dict['attributes']['current_state'] == "offline": if response_dict['attributes']['current_state'] == "offline":
await self.config.guild(interaction.guild).power_action_in_progress.set(True)
for data in updater_startup_vars: for data in updater_startup_vars:
await session.put(await self.get_url(interaction.guild, "startup/variable"), headers=headers, json=data) await self.put(await self.get_url(interaction.guild, "startup/variable"), headers, data)
await session.post(await self.get_url(interaction.guild, "power"), headers=headers, json={"signal": "start"}) requests.post(await self.get_url(interaction.guild, "power"), headers=headers, json={"signal": "start"})
await interaction_message.edit(content="Updater started...") await interaction_message.edit(content="Updater started...")
await asyncio.sleep(1) await asyncio.sleep(1)
while True: while True:
async with session.get(await self.get_url(interaction.guild, "resources"), headers=headers) as response: async with aiohttp.ClientSession() as session:
response_dict = await response.json() async with session.get(await self.get_url(interaction.guild, "resources"), headers=headers) as response:
if response_dict['attributes']['current_state'] == "offline": response_dict = await response.json()
await interaction_message.edit(content="Updater finished.") if response_dict['attributes']['current_state'] == "offline":
break await interaction_message.edit(content="Updater finished.")
else: break
await asyncio.sleep(0.5) else:
continue await asyncio.sleep(1)
continue
for data in old_startup_vars: for data in old_startup_vars:
await session.put(await self.get_url(interaction.guild, "startup/variable"), headers=headers, json=data) await self.put(await self.get_url(interaction.guild, "startup/variable"), headers, data)
await interaction_message.edit(content="Updater finished.\nUpdate process completed!") await interaction_message.edit(content="Updater finished.\nUpdate process completed!")
await self.config.guild(interaction.guild).power_action_in_progress.set(False)
elif response_dict['attributes']['current_state'] == "running" or response_dict['attributes']['current_state'] == "starting": elif response_dict['attributes']['current_state'] == "running" or response_dict['attributes']['current_state'] == "starting":
passed_info = { passed_info = {
"headers": headers, "headers": headers,
@ -128,23 +120,20 @@ class Pterodactyl(commands.Cog):
"interaction": interaction, "interaction": interaction,
"guild": interaction.guild, "guild": interaction.guild,
} }
await interaction_message.edit(content="The server is already running! Are you sure you'd like to stop the server for updates?", view=self.UpdateButtons(timeout=180, passed_info=passed_info, session=session)) await interaction_message.edit(content="The server is already running! Are you sure you'd like to stop the server for updates?", view=self.UpdateButtons(timeout=180, passed_info=passed_info))
power = app_commands.Group(name='power', description="Controls the server's power state.") power = app_commands.Group(name='power', description="Controls the server's power state.")
@power.command(name='start', description="Starts the server.") @power.command(name='start', description="Starts the server.")
@app_commands.guild_only() @power.guild_only()
async def start(self, interaction: discord.Interaction): async def start(self, interaction: discord.Interaction):
"""Starts the server.""" """Starts the server."""
if await self.config.guild(interaction.guild).power_action_in_progress() is True:
await interaction.response.send_message(ephemeral=True, content="Power action already in progress!\nTry again later.")
return
await interaction.response.defer(ephemeral=True, thinking=True) await interaction.response.defer(ephemeral=True, thinking=True)
interaction_message = await interaction.original_response() interaction_message = await interaction.original_response()
headers = await self.get_headers(interaction.guild) headers = await self.get_headers(interaction.guild)
async with self.session.get(await self.get_url(interaction.guild, "resources"), headers=headers) as response: response = requests.get(await self.get_url(interaction.guild, "resources"), headers=headers)
response_json = await response.json() requests_json = response.json()
current_status = response_json['attributes']['current_state'] current_status = requests_json['attributes']['current_state']
if current_status == "offline": if current_status == "offline":
passed_info = { passed_info = {
"headers": headers, "headers": headers,
@ -155,24 +144,21 @@ class Pterodactyl(commands.Cog):
"message": "Server starting...", "message": "Server starting...",
"completed_message": "Server started!" "completed_message": "Server started!"
} }
await interaction_message.edit(content="Are you sure you'd like to start the server?", view=self.PowerButtons(timeout=180, passed_info=passed_info, session=self.session)) await interaction_message.edit(content="Are you sure you'd like to start the server?", view=self.PowerButtons(timeout=180, passed_info=passed_info))
else: else:
message = await interaction_message.edit(content="The server is already running!") message = await interaction_message.edit(content="The server is already running!")
await message.delete(delay=3) await message.delete(delay=3)
@power.command(name='restart', description="Restarts the server.") @power.command(name='restart', description="Restarts the server.")
@app_commands.guild_only() @power.guild_only()
async def restart(self, interaction: discord.Interaction): async def restart(self, interaction: discord.Interaction):
"""Restarts the server.""" """Restarts the server."""
if await self.config.guild(interaction.guild).power_action_in_progress() is True:
await interaction.response.send_message(ephemeral=True, content="Power action already in progress!\nTry again later.")
return
await interaction.response.defer(ephemeral=True, thinking=True) await interaction.response.defer(ephemeral=True, thinking=True)
interaction_message = await interaction.original_response() interaction_message = await interaction.original_response()
headers = await self.get_headers(interaction.guild) headers = await self.get_headers(interaction.guild)
async with self.session.get(await self.get_url(interaction.guild, "resources"), headers=headers) as response: response = requests.get(await self.get_url(interaction.guild, "resources"), headers=headers)
response_json = await response.json() requests_json = response.json()
current_status = response_json['attributes']['current_state'] current_status = requests_json['attributes']['current_state']
if current_status == "running": if current_status == "running":
passed_info = { passed_info = {
"headers": headers, "headers": headers,
@ -183,7 +169,7 @@ class Pterodactyl(commands.Cog):
"message": "Server restarting...", "message": "Server restarting...",
"completed_message": "Server restarted!" "completed_message": "Server restarted!"
} }
await interaction_message.edit(content="Are you sure you'd like to restart the server?", view=self.PowerButtons(timeout=180, passed_info=passed_info, session=self.session)) await interaction_message.edit(content="Are you sure you'd like to restart the server?", view=self.PowerButtons(timeout=180, passed_info=passed_info))
elif current_status == "offline": elif current_status == "offline":
message = await interaction_message.edit(content="The server is offline!") message = await interaction_message.edit(content="The server is offline!")
await message.delete(delay=3) await message.delete(delay=3)
@ -192,18 +178,15 @@ class Pterodactyl(commands.Cog):
await message.delete(delay=3) await message.delete(delay=3)
@power.command(name='stop', description="Stops the server.") @power.command(name='stop', description="Stops the server.")
@app_commands.guild_only() @power.guild_only()
async def stop(self, interaction: discord.Interaction): async def stop(self, interaction: discord.Interaction):
"""Stops the server.""" """Stops the server."""
if await self.config.guild(interaction.guild).power_action_in_progress() is True:
await interaction.response.send_message(ephemeral=True, content="Power action already in progress!\nTry again later.")
return
await interaction.response.defer(ephemeral=True, thinking=True) await interaction.response.defer(ephemeral=True, thinking=True)
interaction_message = await interaction.original_response() interaction_message = await interaction.original_response()
headers = await self.get_headers(interaction.guild) headers = await self.get_headers(interaction.guild)
async with self.session.get(await self.get_url(interaction.guild, "resources"), headers=headers) as response: response = requests.get(await self.get_url(interaction.guild, "resources"), headers=headers)
response_json = await response.json() requests_json = response.json()
current_status = response_json['attributes']['current_state'] current_status = requests_json['attributes']['current_state']
if current_status == "running" or current_status == "starting": if current_status == "running" or current_status == "starting":
passed_info = { passed_info = {
"headers": headers, "headers": headers,
@ -214,62 +197,62 @@ class Pterodactyl(commands.Cog):
"message": "Server stopping...", "message": "Server stopping...",
"completed_message": "Server stopped!" "completed_message": "Server stopped!"
} }
await interaction_message.edit(content="Are you sure you'd like to stop the server?", view=self.PowerButtons(timeout=180, passed_info=passed_info, session=self.session)) await interaction_message.edit(content="Are you sure you'd like to stop the server?", view=self.PowerButtons(timeout=180, passed_info=passed_info))
elif current_status == "offline": elif current_status == "offline":
message = await interaction_message.edit(content="The server is already offline!") message = await interaction_message.edit(content="The server is already offline!")
await message.delete(delay=3) await message.delete(delay=3)
class UpdateButtons(ui.View): class UpdateButtons(ui.View):
def __init__(self, timeout, passed_info, session: aiohttp.ClientSession): def __init__(self, timeout, passed_info):
super().__init__() super().__init__()
self.passed_info = passed_info self.passed_info = passed_info
self.session = session
self.config = Config.get_conf(None, cog_name='Pterodactyl', identifier=457581387213637448123567) self.config = Config.get_conf(None, cog_name='Pterodactyl', identifier=457581387213637448123567)
@ui.button(label="Yes", style=discord.ButtonStyle.success) @ui.button(label="Yes", style=discord.ButtonStyle.success)
async def yes_button(self, button:ui.Button, interaction:discord.Interaction): async def yes_button(self, button:ui.Button, interaction:discord.Interaction):
await self.config.guild(self.passed_info['guild']).power_action_in_progress.set(True) headers = self.passed_info['headers']
session = self.session requests.post(await Pterodactyl.get_url(self, self.passed_info['guild'], "power"), headers=headers, json={"signal": "stop"})
await session.post(await Pterodactyl.get_url(self, self.passed_info['guild'], "power"), headers=self.passed_info['headers'], json={"signal": "stop"})
await self.passed_info['interaction'].edit_original_response(content="Server stopping...", view=None) await self.passed_info['interaction'].edit_original_response(content="Server stopping...", view=None)
while True: while True:
async with session.get(await Pterodactyl.get_url(self, self.passed_info['guild'], "resources"), headers=self.passed_info['headers']) as response: async with aiohttp.ClientSession() as session:
response_dict = await response.json() async with session.get(await Pterodactyl.get_url(self, self.passed_info['guild'], "resources"), headers=headers) as response:
if response_dict['attributes']['current_state'] == "offline": response_dict = await response.json()
await self.passed_info['interaction'].edit_original_response(content="\nServer stopped!") if response_dict['attributes']['current_state'] == "offline":
break await self.passed_info['interaction'].edit_original_response(content="\nServer stopped!")
else: break
await asyncio.sleep(0.5) else:
continue await asyncio.sleep(2)
continue
for data in self.passed_info['updater_startup_vars']: for data in self.passed_info['updater_startup_vars']:
await session.put(url=await Pterodactyl.get_url(self, self.passed_info['guild'], "startup/variable"), headers=self.passed_info['headers'], json=data) await Pterodactyl.put(self, url=await Pterodactyl.get_url(self, self.passed_info['guild'], "startup/variable"), headers=headers, data=data)
await session.post(url=await Pterodactyl.get_url(self, self.passed_info['guild'], "power"), headers=self.passed_info['headers'], json={"signal": "start"}) requests.post(url=await Pterodactyl.get_url(self, self.passed_info['guild'], "power"), headers=headers, json={"signal": "start"})
await self.passed_info['interaction'].edit_original_response(content="Updater started...") await self.passed_info['interaction'].edit_original_response(content="Updater started...")
await asyncio.sleep(2) await asyncio.sleep(2)
while True: while True:
async with session.get(await Pterodactyl.get_url(self, self.passed_info['guild'], "resources"), headers=self.passed_info['headers']) as response: async with aiohttp.ClientSession() as session:
response_dict = await response.json() async with session.get(await Pterodactyl.get_url(self, self.passed_info['guild'], "resources"), headers=headers) as response:
if response_dict['attributes']['current_state'] == "offline": response_dict = await response.json()
await self.passed_info['interaction'].edit_original_response(content="Updater finished!") if response_dict['attributes']['current_state'] == "offline":
break await self.passed_info['interaction'].edit_original_response(content="Updater finished!")
else: break
await asyncio.sleep(1) else:
continue await asyncio.sleep(1)
continue
for data in self.passed_info['old_startup_vars']: for data in self.passed_info['old_startup_vars']:
await session.put(await Pterodactyl.get_url(self, self.passed_info['guild'], "startup/variable"), headers=self.passed_info['headers'], json=data) await Pterodactyl.put(self, await Pterodactyl.get_url(self, self.passed_info['guild'], "startup/variable"), headers, data)
await asyncio.sleep(2) await asyncio.sleep(2)
await session.post(url=await Pterodactyl.get_url(self, self.passed_info['guild'], "power"), headers=self.passed_info['headers'], json={"signal": "start"}) requests.post(url=await Pterodactyl.get_url(self, self.passed_info['guild'], "power"), headers=headers, json={"signal": "start"})
await self.passed_info['interaction'].edit_original_response(content="Server starting...") await self.passed_info['interaction'].edit_original_response(content="Server starting...")
while True: while True:
async with session.get(await Pterodactyl.get_url(self, self.passed_info['guild'], "resources"), headers=self.passed_info['headers']) as response: async with aiohttp.ClientSession() as session:
response_dict = await response.json() async with session.get(await Pterodactyl.get_url(self, self.passed_info['guild'], "resources"), headers=headers) as response:
if response_dict['attributes']['current_state'] == "running": response_dict = await response.json()
await self.passed_info['interaction'].edit_original_response(content="Server started!\nUpdate process completed!") if response_dict['attributes']['current_state'] == "running":
break await self.passed_info['interaction'].edit_original_response(content="Server started!\nUpdate process completed!")
else: break
await asyncio.sleep(0.5) else:
continue await asyncio.sleep(1)
await self.config.guild(self.passed_info['guild']).power_action_in_progress.set(False) continue
@ui.button(label="No", style=discord.ButtonStyle.danger) @ui.button(label="No", style=discord.ButtonStyle.danger)
async def no_button(self, button:ui.Button, interaction:discord.Interaction): async def no_button(self, button:ui.Button, interaction:discord.Interaction):
@ -277,29 +260,26 @@ class Pterodactyl(commands.Cog):
await message.delete(delay=3) await message.delete(delay=3)
class PowerButtons(ui.View): class PowerButtons(ui.View):
def __init__(self, timeout, passed_info, session: aiohttp.ClientSession): def __init__(self, timeout, passed_info):
super().__init__() super().__init__()
self.passed_info = passed_info self.passed_info = passed_info
self.session = session
self.config = Config.get_conf(None, cog_name='Pterodactyl', identifier=457581387213637448123567) self.config = Config.get_conf(None, cog_name='Pterodactyl', identifier=457581387213637448123567)
@ui.button(label="Yes", style=discord.ButtonStyle.success) @ui.button(label="Yes", style=discord.ButtonStyle.success)
async def yes_button(self, button:ui.Button, interaction:discord.Interaction): async def yes_button(self, button:ui.Button, interaction:discord.Interaction):
await self.config.guild(self.passed_info['guild']).power_action_in_progress.set(True)
headers = self.passed_info['headers'] headers = self.passed_info['headers']
await self.session.post(await Pterodactyl.get_url(self, self.passed_info['guild'], "power"), headers=headers, json={"signal": self.passed_info['signal']}) requests.post(await Pterodactyl.get_url(self, self.passed_info['guild'], "power"), headers=headers, json={"signal": self.passed_info['signal']})
message = await self.passed_info['interaction'].edit_original_response(content=self.passed_info['message'], view=None) message = await self.passed_info['interaction'].edit_original_response(content=self.passed_info['message'], view=None)
await asyncio.sleep(10)
while True: while True:
async with self.session.get(await Pterodactyl.get_url(self, self.passed_info['guild'], "resources"), headers=headers) as response: async with aiohttp.ClientSession() as session:
response_dict = await response.json() async with session.get(await Pterodactyl.get_url(self, self.passed_info['guild'], "resources"), headers=headers) as response:
if response_dict['attributes']['current_state'] == self.passed_info['target_signal']: response_dict = await response.json()
await message.edit(content=self.passed_info['completed_message']) if response_dict['attributes']['current_state'] == self.passed_info['target_signal']:
break await message.edit(content=self.passed_info['completed_message'])
else: break
await asyncio.sleep(1) else:
continue await asyncio.sleep(1)
await self.config.guild(self.passed_info['guild']).power_action_in_progress.set(False) continue
@ui.button(label="No", style=discord.ButtonStyle.danger) @ui.button(label="No", style=discord.ButtonStyle.danger)
async def no_button(self, button:ui.Button, interaction:discord.Interaction): async def no_button(self, button:ui.Button, interaction:discord.Interaction):
@ -310,7 +290,7 @@ class Pterodactyl(commands.Cog):
get_group = app_commands.Group(name='get', description="Retrieves information from the Pterodactyl API.") get_group = app_commands.Group(name='get', description="Retrieves information from the Pterodactyl API.")
@get_group.command(name='url', description="Retrieves the URL for the specified endpoint.") @get_group.command(name='url', description="Retrieves the URL for the specified endpoint.")
@app_commands.guild_only() @get_group.guild_only()
async def retrieve_url(self, interaction: discord.Interaction, endpoint: str = None): async def retrieve_url(self, interaction: discord.Interaction, endpoint: str = None):
"""Retrieves the URL for the specified endpoint.""" """Retrieves the URL for the specified endpoint."""
try: try:
@ -326,13 +306,13 @@ class Pterodactyl(commands.Cog):
configure = app_commands.Group(name="config", description="Configures the Pterodactyl cog.") configure = app_commands.Group(name="config", description="Configures the Pterodactyl cog.")
@configure.command(name="api", description="Sets the information used to access the Pterodactyl API.") @configure.command(name="api", description="Sets the information used to access the Pterodactyl API.")
@app_commands.guild_only() @configure.guild_only()
async def configure_api(self, interaction: discord.Interaction): async def configure_api(self, interaction: discord.Interaction):
"""Sets the information used to access the Pterdoactyl API.""" """Sets the information used to access the Pterdoactyl API."""
await interaction.response.send_modal(self.APIConfigModal(self.config)) await interaction.response.send_modal(self.APIConfigModal(self.config))
@configure.command(name="update", description="Sets the startup arguments for the update command.") @configure.command(name="update", description="Sets the startup arguments for the update command.")
@app_commands.guild_only() @configure.guild_only()
async def configure_update(self, interaction: discord.Interaction): async def configure_update(self, interaction: discord.Interaction):
"""Sets the startup arguments for the update command.""" """Sets the startup arguments for the update command."""
await interaction.response.send_modal(self.StartupConfigModal(self.config)) await interaction.response.send_modal(self.StartupConfigModal(self.config))