2023-07-15 12:59:55 -04:00
import asyncio
import aiohttp
2023-07-14 11:29:06 -04:00
import discord
2023-07-15 12:59:55 -04:00
import requests
2023-07-15 19:16:11 -04:00
from discord import ui
2023-07-15 23:39:35 -04:00
from discord . ext import commands
2023-07-14 11:29:06 -04:00
from redbot . core import commands , app_commands , Config
2023-07-14 11:04:58 -04:00
class Pterodactyl ( commands . Cog ) :
""" Pterodactyl allows you to manage your Pterodactyl Panel from Discord. """
def __init__ ( self , bot ) :
self . bot = bot
self . config = Config . get_conf ( self , identifier = 457581387213637448123567 )
2023-07-14 11:08:49 -04:00
self . config . register_guild (
2023-07-14 11:17:38 -04:00
base_url = None ,
2023-07-14 11:04:58 -04:00
api_key = None ,
2023-07-15 12:59:55 -04:00
server_id = None ,
startup_jar = None ,
startup_arguments = None
2023-07-14 11:04:58 -04:00
)
2023-07-14 11:29:06 -04:00
async def get_url ( self , guild , endpoint = None ) :
2023-07-15 23:33:33 -04:00
""" Returns the base url for the server ' s API, or the url for a specific API endpoint if one is provided. """
2023-07-14 12:29:59 -04:00
if await self . config . guild ( guild ) . server_id ( ) is None :
2023-07-14 11:04:58 -04:00
raise LookupError ( " Server ID not set. " )
2023-07-14 12:29:59 -04:00
elif await self . config . guild ( guild ) . base_url ( ) is None :
2023-07-14 11:16:03 -04:00
raise LookupError ( " Base URL not set. " )
2023-07-14 11:29:06 -04:00
base_url = await self . config . guild ( guild ) . base_url ( )
server_id = await self . config . guild ( guild ) . server_id ( )
2023-07-15 23:33:33 -04:00
url = f " https:// { base_url } /api/client/servers/ { server_id } "
2023-07-14 11:04:58 -04:00
if endpoint :
2023-07-15 23:33:33 -04:00
url + = ' / ' + endpoint
2023-07-14 11:04:58 -04:00
return url
2023-07-15 12:59:55 -04:00
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
2023-07-15 18:24:07 -04:00
@app_commands.command ( description = " Updates the server. " )
2023-07-15 12:59:55 -04:00
async def update ( self , interaction : discord . Interaction ) :
2023-07-15 17:50:57 -04:00
""" Updates the server using the arguments provided in the server ' s configuration. """
2023-07-15 17:51:41 -04:00
await interaction . response . defer ( ephemeral = True , thinking = True )
2023-07-15 17:50:57 -04:00
interaction_message = await interaction . original_response ( )
2023-07-15 12:59:55 -04:00
if await self . config . guild ( interaction . guild ) . api_key ( ) is None :
2023-07-15 17:50:57 -04:00
await interaction_message . edit ( f " Something went wrong. \n Error: `API Key not set.` " , ephemeral = True )
2023-07-15 12:59:55 -04:00
raise LookupError ( " API Key not set. " )
elif await self . config . guild ( interaction . guild ) . startup_jar ( ) is None :
2023-07-15 17:50:57 -04:00
await interaction_message . edit ( f " Something went wrong. \n Error: `Startup jar not set.` " , ephemeral = True )
2023-07-15 12:59:55 -04:00
raise LookupError ( " Startup jar not set. " )
elif await self . config . guild ( interaction . guild ) . startup_arguments ( ) is None :
2023-07-15 17:42:16 -04:00
await interaction . response . send_message ( f " Something went wrong. \n Error: `Startup arguments not set.` " , ephemeral = True )
2023-07-15 12:59:55 -04:00
raise LookupError ( " Startup arguments not set. " )
else :
api_key = await self . config . guild ( interaction . guild ) . api_key ( )
startup_jar = await self . config . guild ( interaction . guild ) . startup_jar ( )
startup_commands = await self . config . guild ( interaction . guild ) . startup_arguments ( )
headers = {
" Authorization " : f " Bearer { api_key } " ,
" Content-Type " : " application/json " ,
2023-07-15 23:14:44 -04:00
" Accept " : " application/json "
2023-07-15 12:59:55 -04:00
}
2023-07-15 15:36:57 -04:00
response = requests . get ( await self . get_url ( interaction . guild , " resources " ) , headers = headers )
2023-07-15 12:59:55 -04:00
response_dict = response . json ( )
2023-07-15 15:36:57 -04:00
list_var = requests . get ( await self . get_url ( interaction . guild , " startup " ) , headers = headers )
2023-07-15 12:59:55 -04:00
list_var_response_dict = list_var . json ( )
2023-07-15 21:03:22 -04:00
updater_startup_vars = [
2023-07-15 12:59:55 -04:00
{
" key " : " FLAGS " ,
" value " : startup_commands
} ,
{
" key " : " SERVER_JARFILE " ,
" value " : startup_jar
}
]
2023-07-15 21:03:22 -04:00
old_startup_vars = [
2023-07-15 18:32:55 -04:00
{
" key " : " FLAGS " ,
2023-07-15 21:02:55 -04:00
" value " : list_var_response_dict [ ' data ' ] [ 4 ] [ ' attributes ' ] [ ' server_value ' ]
2023-07-15 18:32:55 -04:00
} ,
{
" key " : " SERVER_JARFILE " ,
2023-07-15 21:02:55 -04:00
" value " : list_var_response_dict [ ' data ' ] [ 0 ] [ ' attributes ' ] [ ' server_value ' ]
2023-07-15 18:32:55 -04:00
}
]
2023-07-15 12:59:55 -04:00
if response_dict [ ' attributes ' ] [ ' current_state ' ] == " offline " :
2023-07-15 21:03:22 -04:00
for data in updater_startup_vars :
2023-07-15 15:36:57 -04:00
await self . put ( await self . get_url ( interaction . guild , " startup/variable " ) , headers , data )
requests . post ( await self . get_url ( interaction . guild , " power " ) , headers = headers , json = { " signal " : " start " } )
2023-07-15 18:09:33 -04:00
await interaction_message . edit ( content = " Packwiz installer started... " )
2023-07-15 18:28:32 -04:00
await asyncio . sleep ( 1 )
2023-07-15 12:59:55 -04:00
while True :
async with aiohttp . ClientSession ( ) as session :
2023-07-15 15:36:57 -04:00
async with session . get ( await self . get_url ( interaction . guild , " resources " ) , headers = headers ) as response :
2023-07-15 12:59:55 -04:00
response_dict = await response . json ( )
if response_dict [ ' attributes ' ] [ ' current_state ' ] == " offline " :
2023-07-15 18:23:25 -04:00
await interaction_message . edit ( content = " Packwiz installer finished. " )
2023-07-15 12:59:55 -04:00
break
else :
await asyncio . sleep ( 1 )
continue
2023-07-15 21:03:22 -04:00
for data in old_startup_vars :
2023-07-15 15:36:57 -04:00
await self . put ( await self . get_url ( interaction . guild , " startup/variable " ) , headers , data )
2023-07-15 18:25:04 -04:00
await interaction_message . edit ( content = " Packwiz installer finished. \n Update process completed! " )
2023-07-15 17:42:16 -04:00
elif response_dict [ ' attributes ' ] [ ' current_state ' ] == " running " or response_dict [ ' attributes ' ] [ ' current_state ' ] == " starting " :
2023-07-15 21:03:22 -04:00
passed_info = {
" headers " : headers ,
" updater_startup_vars " : updater_startup_vars ,
" old_startup_vars " : old_startup_vars ,
2023-07-15 23:39:35 -04:00
" interaction " : interaction ,
2023-07-15 23:42:12 -04:00
" guild " : interaction . guild ,
2023-07-15 21:03:22 -04:00
}
2023-07-15 18:57:30 -04:00
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 ) )
2023-07-15 12:59:55 -04:00
2023-07-15 19:16:11 -04:00
class UpdateButtons ( ui . View ) :
2023-07-15 12:59:55 -04:00
def __init__ ( self , timeout , passed_info ) :
super ( ) . __init__ ( )
self . passed_info = passed_info
2023-07-16 08:17:56 -04:00
self . config = Config . get_conf ( None , cog_name = ' Pterodactyl ' , identifier = 457581387213637448123567 )
2023-07-15 12:59:55 -04:00
2023-07-15 19:16:11 -04:00
@ui.button ( label = " Yes " , style = discord . ButtonStyle . success )
async def yes_button ( self , button : ui . Button , interaction : discord . Interaction ) :
2023-07-16 07:58:33 -04:00
requests . post ( await Pterodactyl . get_url ( self , self . passed_info [ ' guild ' ] , " power " ) , headers = self . passed_info [ ' headers ' ] , json = { " signal " : " stop " } )
2023-07-16 08:19:55 -04:00
await self . passed_info [ ' interaction ' ] . edit_original_response ( content = " Server stopping... " , view = None )
2023-07-15 18:23:25 -04:00
while True :
async with aiohttp . ClientSession ( ) as session :
2023-07-16 08:21:45 -04:00
async with session . get ( await Pterodactyl . get_url ( self , self . passed_info [ ' guild ' ] , " resources " ) , headers = self . passed_info [ ' headers ' ] ) as response :
2023-07-15 18:23:25 -04:00
response_dict = await response . json ( )
if response_dict [ ' attributes ' ] [ ' current_state ' ] == " offline " :
2023-07-16 08:19:55 -04:00
await self . passed_info [ ' interaction ' ] . edit_original_response ( content = " \n Server stopped! " )
2023-07-15 18:23:25 -04:00
break
else :
2023-07-15 18:53:38 -04:00
await asyncio . sleep ( 2 )
2023-07-15 18:23:25 -04:00
continue
2023-07-15 21:03:22 -04:00
for data in self . passed_info [ ' updater_startup_vars ' ] :
2023-07-16 08:25:51 -04:00
await Pterodactyl . put ( self , url = await Pterodactyl . get_url ( self , self . passed_info [ ' guild ' ] , " startup/variable " ) , headers = self . passed_info [ ' headers ' ] , data = data )
requests . post ( url = await Pterodactyl . get_url ( self , self . passed_info [ ' guild ' ] , " power " ) , headers = self . passed_info [ ' headers ' ] , json = { " signal " : " start " } )
2023-07-16 08:19:55 -04:00
await self . passed_info [ ' interaction ' ] . edit_original_response ( content = " Packwiz installer started... " )
2023-07-16 08:30:03 -04:00
await asyncio . sleep ( 2 )
2023-07-15 12:59:55 -04:00
while True :
async with aiohttp . ClientSession ( ) as session :
2023-07-16 08:21:45 -04:00
async with session . get ( await Pterodactyl . get_url ( self , self . passed_info [ ' guild ' ] , " resources " ) , headers = self . passed_info [ ' headers ' ] ) as response :
2023-07-15 12:59:55 -04:00
response_dict = await response . json ( )
if response_dict [ ' attributes ' ] [ ' current_state ' ] == " offline " :
2023-07-16 08:19:55 -04:00
await self . passed_info [ ' interaction ' ] . edit_original_response ( content = " Packwiz installer finished! " )
2023-07-15 12:59:55 -04:00
break
else :
await asyncio . sleep ( 1 )
continue
2023-07-15 21:03:22 -04:00
for data in self . passed_info [ ' old_startup_vars ' ] :
2023-07-16 08:25:51 -04:00
await Pterodactyl . put ( self , await Pterodactyl . get_url ( self , self . passed_info [ ' guild ' ] , " startup/variable " ) , self . passed_info [ ' headers ' ] , data )
2023-07-16 08:30:03 -04:00
await asyncio . sleep ( 2 )
2023-07-16 08:21:45 -04:00
requests . post ( await Pterodactyl . get_url ( self , self . passed_info [ ' guild ' ] , " power " ) , self . passed_info [ ' headers ' ] , json = { " signal " : " start " } )
2023-07-16 08:19:55 -04:00
await self . passed_info [ ' interaction ' ] . edit_original_response ( content = " Server starting... " )
2023-07-15 12:59:55 -04:00
while True :
async with aiohttp . ClientSession ( ) as session :
2023-07-16 08:21:45 -04:00
async with session . get ( await Pterodactyl . get_url ( self , self . passed_info [ ' guild ' ] , " resources " ) , headers = self . passed_info [ ' headers ' ] ) as response :
2023-07-15 12:59:55 -04:00
response_dict = await response . json ( )
if response_dict [ ' attributes ' ] [ ' current_state ' ] == " running " :
2023-07-16 08:19:55 -04:00
await self . passed_info [ ' interaction ' ] . edit_original_response ( content = " Server started! \n Update process completed! " )
2023-07-15 12:59:55 -04:00
break
else :
await asyncio . sleep ( 1 )
continue
2023-07-15 19:16:11 -04:00
@ui.button ( label = " No " , style = discord . ButtonStyle . danger )
async def no_button ( self , button : ui . Button , interaction : discord . Interaction ) :
2023-07-15 23:12:33 -04:00
message = await self . passed_info [ ' interaction ' ] . edit_original_response ( content = f " Command cancelled. " , view = None )
2023-07-15 23:12:47 -04:00
await message . delete ( delay = 3 )
2023-07-15 12:59:55 -04:00
2023-07-14 11:29:06 -04:00
@app_commands.command ( )
async def test ( self , interaction : discord . Interaction , endpoint : str = None ) :
2023-07-14 11:04:58 -04:00
""" This does stuff! """
2023-07-14 11:16:03 -04:00
try :
if endpoint :
2023-07-14 11:29:06 -04:00
url = await self . get_url ( interaction . guild , endpoint )
2023-07-14 11:16:03 -04:00
else :
2023-07-14 11:29:06 -04:00
url = await self . get_url ( interaction . guild )
2023-07-14 11:16:03 -04:00
except LookupError as e :
2023-07-14 11:29:06 -04:00
await interaction . response . send_message ( f " Something went wrong. \n Error: ` { e } ` " , ephemeral = True )
2023-07-14 11:16:58 -04:00
return
2023-07-14 11:29:06 -04:00
await interaction . response . send_message ( url , ephemeral = True )
2023-07-14 11:51:46 -04:00
2023-07-15 12:59:55 -04:00
configure = app_commands . Group ( name = " config " , description = " Configures the Pterodactyl cog. " )
@configure.command ( )
async def api ( self , interaction : discord . Interaction ) :
""" Sets the information used to access the Pterdoactyl API. """
await interaction . response . send_modal ( self . APIConfigModal ( self . config ) )
@configure.command ( )
async def startup ( self , interaction : discord . Interaction ) :
""" Sets the startup arguments for the update command. """
await interaction . response . send_modal ( self . StartupConfigModal ( self . config ) )
2023-07-14 11:51:46 -04:00
2023-07-15 12:59:55 -04:00
class APIConfigModal ( discord . ui . Modal , title = " Pterodactyl Manager Configuration " ) :
2023-07-14 12:27:03 -04:00
def __init__ ( self , config ) :
2023-07-14 12:12:56 -04:00
super ( ) . __init__ ( )
2023-07-14 12:22:28 -04:00
self . config = config
2023-07-14 12:12:56 -04:00
base_url = discord . ui . TextInput (
label = " Base URL " ,
placeholder = " Input your Pterodactyl Panel ' s Base URL here, without HTTPS or HTTP. " ,
style = discord . TextStyle . paragraph ,
required = False ,
max_length = 300
)
api_key = discord . ui . TextInput (
label = " API Key " ,
placeholder = " Input your Pterodactyl Client API Key here. " ,
style = discord . TextStyle . short ,
required = False ,
max_length = 300
)
server_id = discord . ui . TextInput (
label = " Server ID " ,
placeholder = " Input your Pterodactyl server ' s Server ID here. " ,
style = discord . TextStyle . short ,
required = False ,
max_length = 300
)
2023-07-14 11:51:46 -04:00
2023-07-14 12:12:56 -04:00
async def on_submit ( self , interaction : discord . Interaction ) :
message = " "
if self . base_url . value != " " :
2023-07-14 12:32:28 -04:00
await self . config . guild ( interaction . guild ) . base_url . set ( self . base_url . value )
2023-07-15 13:22:10 -04:00
message + = f " - Base URL set to \n - ` { self . base_url . value } ` \n "
2023-07-14 12:12:56 -04:00
if self . api_key . value != " " :
2023-07-14 12:32:28 -04:00
await self . config . guild ( interaction . guild ) . api_key . set ( self . api_key . value )
2023-07-15 13:22:10 -04:00
trimmed_api_key = self . api_key . value [ : 16 ]
message + = f " - API Key set to \n - ` { trimmed_api_key } ` - Trimmed for security \n "
2023-07-14 12:12:56 -04:00
if self . server_id . value != " " :
2023-07-14 12:32:28 -04:00
await self . config . guild ( interaction . guild ) . server_id . set ( self . server_id . value )
2023-07-15 13:22:10 -04:00
message + = f " - Server ID set to \n - ` { self . server_id . value } ` \n "
2023-07-14 12:12:56 -04:00
if message == " " :
2023-07-15 13:22:51 -04:00
trimmed_api_key = str ( await self . config . guild ( interaction . guild ) . api_key ( ) ) [ : 16 ]
2023-07-15 13:22:10 -04:00
send = f " No changes were made. \n Current configuration: \n - Base URL: \n - ` { await self . config . guild ( interaction . guild ) . base_url ( ) } ` \n - API Key: \n - ` { trimmed_api_key } ` - Trimmed for security \n - Server ID: \n - ` { await self . config . guild ( interaction . guild ) . server_id ( ) } ` "
2023-07-15 13:04:03 -04:00
else :
send = f " Configuration changed: \n { message } "
await interaction . response . send_message ( send , ephemeral = True )
2023-07-15 12:59:55 -04:00
class StartupConfigModal ( discord . ui . Modal , title = " Pterodactyl Manager Configuration " ) :
def __init__ ( self , config ) :
super ( ) . __init__ ( )
self . config = config
startup_jar = discord . ui . TextInput (
label = " Startup .jar " ,
placeholder = " Input the name of your updater .jar here. " ,
style = discord . TextStyle . short ,
required = False ,
max_length = 300
)
startup_arguments = discord . ui . TextInput (
label = " Startup Arguments " ,
placeholder = " Input your startup arguments here. \n Example: \n -g -s server https://repository.link/here " ,
style = discord . TextStyle . paragraph ,
required = False ,
max_length = 1000
)
async def on_submit ( self , interaction : discord . Interaction ) :
message = " "
if self . startup_jar . value != " " :
await self . config . guild ( interaction . guild ) . startup_jar . set ( self . startup_jar . value )
2023-07-15 13:22:10 -04:00
message + = f " - Startup jar set to \n - ` { self . startup_jar . value } ` \n "
2023-07-15 12:59:55 -04:00
if self . startup_arguments . value != " " :
await self . config . guild ( interaction . guild ) . startup_arguments . set ( self . startup_arguments . value )
2023-07-15 13:05:33 -04:00
message + = f " - Startup arguments set to: \n ``` { self . startup_arguments . value } ``` \n "
2023-07-15 12:59:55 -04:00
if message == " " :
2023-07-15 13:04:03 -04:00
send = f " No changes were made. \n Current configuration: \n - Startup jar: ` { await self . config . guild ( interaction . guild ) . startup_jar ( ) } ` \n - Startup arguments: \n ``` { await self . config . guild ( interaction . guild ) . startup_arguments ( ) } ``` "
else :
send = f " Configuration changed: \n { message } "
await interaction . response . send_message ( send , ephemeral = True )