Skip to content

Commit

Permalink
refactor for modularity (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
SyniRon authored Sep 23, 2023
1 parent d6a5118 commit 49e0fa0
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 124 deletions.
113 changes: 113 additions & 0 deletions cogs/server_management.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import aiohttp
import asyncio
import logging
import discord
import traceback
from discord import app_commands
import json
import os

class ServerManagement:

def __init__(self, client, tree, sypolt):
self.client = client
self.discord_id = int(os.getenv("DISCORD_ID"))
with open('./config/config.json') as f:
self.servers_dict = json.load(f)["servers_dict"]
self.api_key = os.getenv("API_KEY")
self.sypolt = sypolt
self.tree = tree

@self.tree.command(
name="restart_server",
description="For authorized persons to restart Arma and Squad servers",
guild=discord.Object(id=self.discord_id),
)
@app_commands.describe(servers="Servers to choose from")
@app_commands.choices(
servers=[
discord.app_commands.Choice(name="A3 TacR", value=1),
discord.app_commands.Choice(name="A3 Training 1", value=2),
discord.app_commands.Choice(name="A3 Training 2", value=3),
discord.app_commands.Choice(name="A3 Training 3", value=4),
discord.app_commands.Choice(name="Sq Training", value=5),
discord.app_commands.Choice(name="Sq TacR", value=6),
]
)
async def restart_server(interaction, servers: app_commands.Choice[int]):
"""Restart a server."""
await interaction.response.defer()
username = interaction.user.name
logging.info(f"{username} initiated the restart_server command.")

# Get the server ID based on the chosen server name
server_id = self.servers_dict[servers.name]

# Prepare the API request URL and headers
url = f"https://panel.7cav.us/api/client/servers/{server_id}/power"
headers = {
"Authorization": f"Bearer {self.api_key}",
"Accept": "application/json",
"Content-Type": "application/json",
}

# Prepare payload to kill the server process
payload_kill = '{"signal": "kill"}'

# Prepare payload to start the server process
payload_start = '{"signal": "start"}'

# Send a request to the server
async with aiohttp.ClientSession() as session:
try:
async with session.post(url, headers=headers, data=payload_kill) as response:
if response.status == 204:
await asyncio.sleep(5)

async with session.post(url, headers=headers, data=payload_start) as response:
if response.status == 204:
logging.info(f"Server {servers.name} restarted successfully!")
await interaction.followup.send(f"✅ Server {servers.name} restarted successfully!")
await sypolt.send(
f"DEBUG: {username} used /restart_server and {servers.name} restarted successfully!")
else:
logging.error(f"Server start failed on {servers.name}.")
await interaction.followup.send(f"❌ Server start failed on {servers.name}.")
await sypolt.send(
"DEBUG:{username} used /restart_server and server start failed on {servers.name}.")
else:
logging.error(f"Failed to stop server {servers.name}.")
await interaction.followup.send(f"❌ Failed to stop server {servers.name}.")
await sypolt.send(
f"DEBUG: {username} used /restart_server and failed to stop server {servers.name}.")
except aiohttp.ClientError as e:
logging.error(f"Network error: {e}")
await sypolt.send(f"DEBUG: {username} used /restart_server and network error: {e}")
await interaction.followup.send(f"❌ Network error: {e}")
except asyncio.TimeoutError:
logging.error("API request timed out.")
await sypolt.send(f"DEBUG: {username} used /restart_server and API request timed out.")
await interaction.followup.send("❌ API request timed out.")
except Exception as e:
logging.error(f"An unexpected error occurred: {e}")
await interaction.followup.send(f"❌ An unexpected error occurred: {e}")
error_info = f"Traceback: {traceback.format_exc()}, User: {interaction.user.id}"
await sypolt.send(f"An unexpected error occurred: {e}, {error_info}")
@self.tree.error
async def on_command_error(
interaction: discord.Interaction, error: discord.app_commands.AppCommandError
) -> None:
"""Handle errors from the command tree."""
# Forward the error message to the bot owner and respond with a generic error message
logging.error(f"Sypolt Send! Error: {error}")
await sypolt.send(
f"Someone broke your bot in a new and interesting way ```{error}```"

)
await interaction.followup.send(
"You managed to break the bot in a way I didn't expect, good job. If the Cav \
had an Army Bug Finder medal I'd give it to you. Anyway, the error was forwarded to me, Sypolt.R.",
ephemeral = False,

)

10 changes: 10 additions & 0 deletions config/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"servers_dict": {
"A3 TacR": "d7291177",
"A3 Training 1": "9fd28d83",
"A3 Training 2": "6b20f439",
"A3 Training 3": "7b217461",
"Sq Training": "7e1451cc",
"Sq TacR": "0b493e80"
}
}
130 changes: 6 additions & 124 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import asyncio
import os

import aiohttp
import discord
from discord import app_commands
import logging
import traceback

from cogs.server_management import ServerManagement

if not all(os.getenv(var) for var in ["DISCORD_ID", "API_KEY", "OWNER_ID", "BOT_TOKEN"]):
logging.error("Missing essential environment variables.")
Expand All @@ -26,135 +25,18 @@
# Set the bot's status activity
status = discord.Activity(name="https://7cav.us", type=3)

discord_id = int(os.getenv("DISCORD_ID"))
owner_id = int(os.getenv("OWNER_ID"))


# Define a dictionary mapping server names to their corresponding server IDs
servers_dict = {
"A3 TacR": "d7291177",
"A3 Training 1": "9fd28d83",
"A3 Training 2": "6b20f439",
"A3 Training 3": "7b217461",
"Sq Training": "7e1451cc",
"Sq TacR": "0b493e80",
}

# Retrieve the API key from the environment variable
api_key = os.getenv("API_KEY")


# Error handler for command errors
@tree.error
async def on_command_error(
interaction: discord.Interaction, error: discord.app_commands.AppCommandError
) -> None:
if isinstance(error, app_commands.errors.MissingAnyRole):
# Respond with an error message if the user doesn't have the required role
logging.error(f"Missing role! Error: {error}")
await interaction.response.send(
f"You must have one of these roles: `{error.missing_roles}` in order to use this command",
ephemeral=True,
)
else:
# Forward the error message to the bot owner and respond with a generic error message
logging.error(f"Sypolt Send! Error: {error}")
await sypolt.send(
f"Someone broke your bot in a new and interesting way ```{error}```"
)
await interaction.followup.send(
"You managed to break the bot in a way I didn't expect, good job. If the Cav \
had an Army Bug Finder medal I'd give it to you. Anyway, the error was forwarded to me, Sypolt.R. \
Why don't you try whatever that was again but with less breaking things this time?",
ephemeral=False,
)


# Command decorator for the restart_server command
@tree.command(
name="restart_server",
description="For authorized persons to restart Arma and Squad servers",
guild=discord.Object(id=discord_id),
)
@app_commands.describe(servers="Servers to choose from")
@app_commands.choices(
servers=[
discord.app_commands.Choice(name="A3 TacR", value=1),
discord.app_commands.Choice(name="A3 Training 1", value=2),
discord.app_commands.Choice(name="A3 Training 2", value=3),
discord.app_commands.Choice(name="A3 Training 3", value=4),
discord.app_commands.Choice(name="Sq Training", value=5),
discord.app_commands.Choice(name="Sq TacR", value=6),
]
)
async def restart_server(interaction, servers: discord.app_commands.Choice[int]):
# Defer the response to acknowledge the command
await interaction.response.defer()
username = interaction.user.name
logging.info(f"{username} initiated the restart_server command.")

# Get the server ID based on the chosen server name
server_id = servers_dict[servers.name]

# Prepare the API request URL and headers
url = f"https://panel.7cav.us/api/client/servers/{server_id}/power"
headers = {
"Authorization": f"Bearer {api_key}",
"Accept": "application/json",
"Content-Type": "application/json",
}

# Prepare payload to kill the server process
payload_kill = '{"signal": "kill"}'

# Prepare payload to start the server process
payload_start = '{"signal": "start"}'

# Send a request to the server
async with aiohttp.ClientSession() as session:
try:
async with session.post(url, headers=headers, data=payload_kill) as response:
if response.status == 204:
await asyncio.sleep(5)

async with session.post(url, headers=headers, data=payload_start) as response:
if response.status == 204:
logging.info(f"Server {servers.name} restarted successfully!")
await interaction.followup.send(f"✅ Server {servers.name} restarted successfully!")
await sypolt.send(f"DEBUG: {username} used /restart_server and {servers.name} restarted successfully!")
else:
logging.error(f"Server start failed on {servers.name}.")
await interaction.followup.send(f"❌ Server start failed on {servers.name}.")
await sypolt.send("DEBUG:{username} used /restart_server and server start failed on {servers.name}.")
else:
logging.error(f"Failed to stop server {servers.name}.")
await interaction.followup.send(f"❌ Failed to stop server {servers.name}.")
await sypolt.send(f"DEBUG: {username} used /restart_server and failed to stop server {servers.name}.")
except aiohttp.ClientError as e:
logging.error(f"Network error: {e}")
await sypolt.send(f"DEBUG: {username} used /restart_server and network error: {e}")
await interaction.followup.send(f"❌ Network error: {e}")
except asyncio.TimeoutError:
logging.error("API request timed out.")
await sypolt.send(f"DEBUG: {username} used /restart_server and API request timed out.")
await interaction.followup.send("❌ API request timed out.")
except Exception as e:
logging.error(f"An unexpected error occurred: {e}")
await interaction.followup.send(f"❌ An unexpected error occurred: {e}")
error_info = f"Traceback: {traceback.format_exc()}, User: {interaction.user.id}"
await sypolt.send(f"An unexpected error occurred: {e}, {error_info}")


# Event handler for when the bot is ready
@client.event
async def on_ready():
global sypolt
owner_id = int(os.getenv("OWNER_ID"))
discord_id = int(os.getenv("DISCORD_ID"))
sypolt = await client.fetch_user(owner_id)
server_management = ServerManagement(client, tree, sypolt)
# Set the bot's status activity
await client.change_presence(activity=status)

# Sync the command tree with the guild
await tree.sync(guild=discord.Object(id=discord_id))
await server_management.tree.sync(guild=discord.Object(id=discord_id))

# Print a message to indicate the bot is ready
logging.info("Bot ready.")
Expand Down

0 comments on commit 49e0fa0

Please sign in to comment.