Skip to content

Commit

Permalink
Major overhaul of server querying and logging logic
Browse files Browse the repository at this point in the history
  • Loading branch information
mylesagray committed Jun 25, 2024
1 parent 485e547 commit c4250d4
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 43 deletions.
8 changes: 6 additions & 2 deletions app/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,9 @@ async def _shutdown(ctx):
the function not being called via sudo
"""
try:
if gamequery.is_anyone_active() and not ctx.author == 'sudo':
#TODO:myles - Add failed server query feedback to user
is_anyone_active = gamequery.is_anyone_active()
if is_anyone_active[0] and not ctx.author == 'sudo':
await ctx.respond('Server can\'t be shut down, someone is online!')
else:
response = requests.get(SHUTDOWN_URL, timeout=2)
Expand All @@ -199,7 +201,9 @@ async def _reboot(ctx):
the function not being called via sudo
"""
try:
if gamequery.is_anyone_active() and not ctx.author == 'sudo':
#TODO:myles - Add failed server query feedback to user
is_anyone_active = gamequery.is_anyone_active()
if is_anyone_active[0] and not ctx.author == 'sudo':
await ctx.respond('Server can\'t be rebooted, someone is online!')
else:
response = requests.get(REBOOT_URL, timeout=2)
Expand Down
130 changes: 94 additions & 36 deletions app/gamequery.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,35 @@
import network
from servers import Server, ServerType, list_servers, load_servers, get_server

logger = logging.getLogger(__name__)
logging.basicConfig(filename='gamequery.log', level=logging.INFO)
logger.setLevel(logging.DEBUG)

def is_anyone_active() -> bool:
def is_anyone_active() -> tuple[bool, list]:
"""
Checks all known servers for users currently logged in
returns a bool
returns a tuple of a bool and a list
"""
try:
player_count = 0
if list_servers() == {}:
load_servers()
failed_queries = []
for server in list_servers():
server = get_server(server)
player_count += get_players(server).get('current_players')
try:
server = get_server(server)
player_count += get_players(server).get('current_players')
except AttributeError:
logger.warning("No result, couldn't connect to %s, moving on...", server['name'])
failed_queries.append(server['name'])
except Exception:
traceback.print_exc()
if player_count > 0:
return True
return True, failed_queries
else:
return False
return False, failed_queries
except:
logging.error("Couldn't query servers for active players")
logger.error("Couldn't query servers for active players")
traceback.print_exc()
raise

Expand All @@ -39,29 +49,30 @@ def get_players(server: Server) -> dict:
to the server as well as the max players supported
"""
if server['server_type'] is ServerType.STEAM:
logger.info("Querying ArmA server: %s", server['name'])
try:
steamquery = _steam_server_connection(
server_ip=str(server['ip_address']), port=server['port'])
server_state = _lint_steamquery_output(
steamquery.query_server_info())
logger.info("%s has %s/%s players active", server['name'], server_state['players'], server_state['max_players'])
return {"current_players": server_state["players"],
"max_players": server_state["max_players"]}
except ConnectionError:
print("Could not connect to ArmA server, connection error")
traceback.print_exc()
pass
logger.error("Could not connect to %s, connection error", server['name'])
except Exception:
print("Could not get ArmA server info")
logger.error("Could not get %s player info", server['name'])
traceback.print_exc()
raise

elif server['server_type'] is ServerType.SPACE_ENGINEERS:
logger.info("Querying Space Engineers server: %s", server['name'])
try:
server_api_address = "http://" + \
str(server['ip_address']) + ":" + str(server['port'])
api = VRageAPI(url=server_api_address, token=server['password'])
players = api.get_players()
server_ping = api.get_server_ping()
players = api.get_players()
server_info = api.get_server_info()

if server_ping["data"]["Result"] == "Pong":
Expand All @@ -78,72 +89,73 @@ def get_players(server: Server) -> dict:
for player in players["data"]["Players"]:
print(player["SteamID"], player["DisplayName"])

logger.info("%s has %s/%s players active", str(server['name']), player_count, "99")
return {"current_players": player_count, "max_players": 99}
except ConnectionError:
print("Could not connect to SE server, connection error")
traceback.print_exc()
pass
logger.error("Could not connect to %s, connection error", server['name'])
except Exception:
print("Could not get Space Engineers server info")
logger.error("Could not get %s player info", server['name'])
traceback.print_exc()
raise

elif server['server_type'] is ServerType.MINECRAFT_JAVA:
logger.info("Querying Minecraft Java server: %s", server['name'])
try:
server = JavaServer(str(server['ip_address']), server['port'])
status = server.status()
serverinstance = JavaServer(str(server['ip_address']), server['port'])
status = serverinstance.status()
logger.info("%s has %s/%s players active", str(server['name']), status.players.online, status.players.max)
return {"current_players": status.players.online,
"max_players": status.players.max}
except ConnectionError:
print("Could not connect to Minecraft Java server, connection error")
traceback.print_exc()
pass
logger.error("Could not connect to %s, connection error", server['name'])
except Exception:
print("Could not get Minecraft JE server info")
logger.error("Could not get %s player info", server['name'])
traceback.print_exc()
raise

elif server['server_type'] is ServerType.MINECRAFT_BEDROCK:
logger.info("Querying Minecraft Bedrock server: %s", server['name'])
try:
server = BedrockServer(str(server['ip_address']), server['port'])
status = server.status()
serverinstance = BedrockServer(str(server['ip_address']), server['port'])
status = serverinstance.status()
logger.info("%s has %s/%s players active", str(server['name']), status.players.online, status.players.max)
return {"current_players": status.players.online,
"max_players": status.players.max}
except ConnectionError:
print("Could not connect to Minecraft Bedrock server, connection error")
traceback.print_exc()
pass
logger.error("Could not connect to %s, connection error", server['name'])
except Exception:
print("Could not get Minecraft Bedrock server info")
logger.error("Could not get %s player info", server['name'])
traceback.print_exc()
raise

elif server['server_type'] is ServerType.DCS:
##TODO myles
logger.info("%s has 0/0 players active", server['name'])
return {"current_players": 0,
"max_players": 0}

else:
print(f'Cannot query unrecognised server type {server_type}')
logger.error('Cannot query unrecognised server type %s', server_type)


def get_players_details(server: Server) -> list:
"""
Returns a list with all current player objects containing
names, scores and durations on the server
"""
logger.info("Getting player details for %s", server['name'])
if server['server_type'] is ServerType.STEAM:
try:
steamquery = _steam_server_connection(
server_ip=str(server['ip_address']), port=server['port'])
player_info = _lint_steamquery_output(
steamquery.query_player_info())
return player_info

except Exception:
print("Could not get player info")
logger.warning("Could not get player info")
traceback.print_exc()
raise

elif server['server_type'] is ServerType.SPACE_ENGINEERS:
pass

Expand All @@ -159,15 +171,57 @@ def get_players_details(server: Server) -> list:
pass

else:
print(f'Cannot query unrecognised server type {server_type}')
logger.error('Cannot query unrecognised server type %s', server_type)

def get_server_details(server: Server) -> list:
"""
Returns a list with all relevant server config
"""
logger.info("Getting server details for %s", server['name'])
if server['server_type'] is ServerType.STEAM:
try:
steamquery = _steam_server_connection(
server_ip=str(server['ip_address']), port=server['port'])
server_info = _lint_steamquery_output(
steamquery.query_server_info())
return server_info

except Exception:
logger.warning("Could not get server info for %s", server['name'])
traceback.print_exc()
raise
elif server['server_type'] is ServerType.SPACE_ENGINEERS:
pass

# Creates and returns server connection object
elif server['server_type'] is ServerType.DCS:
pass

elif server['server_type'] is ServerType.MINECRAFT_JAVA:
#https://mcstatus.readthedocs.io/en/stable/api/basic/#mcstatus.status_response.JavaStatusPlayer
#https://mcstatus.readthedocs.io/en/stable/api/basic/#mcstatus.querier.QueryResponse
try:
serverinstance = JavaServer(str(server['ip_address']), server['port'])
query = serverinstance.query()
print(query)
except TimeoutError:
logger.error("Could not query server info for %s, timed out", server['name'])
except Exception as error:
logger.error("Could not query server info for %s, %s", server['name'], error)
traceback.print_exc()
raise

elif server['server_type'] is ServerType.MINECRAFT_BEDROCK:
pass

else:
logger.error('Cannot query unrecognised server type %s', server_type)


def _steam_server_connection(server_ip: str, port: int) -> object:
"""
Creates a steam query server connection object and passes it back.
"""
logger.info("Connecting to SteamQuery...")
try:
# Check if IP address is valid
if not network.valid_ip_address(server_ip):
Expand All @@ -178,12 +232,12 @@ def _steam_server_connection(server_ip: str, port: int) -> object:
raise ValueError("PORT environment variable is invalid")

# Construct SteamQuery session
print(f'Connecting to {server_ip}:{port}')
logger.info('Connecting to %s:%s',server_ip , port)
server = SteamQuery(server_ip, port)
return server

except Exception:
print("Unable to connect to server")
logger.warning("Unable to connect to server")
traceback.print_exc()
raise

Expand All @@ -199,6 +253,7 @@ def _lint_steamquery_output(query) -> object:
#
# If the query is a list, then it is a valid response
# in any case
logger.info("Linting SteamQuery ouput")
if isinstance(query, list):
return query
else:
Expand All @@ -207,7 +262,10 @@ def _lint_steamquery_output(query) -> object:
raise ConnectionError(str(query))
else:
return query
except ConnectionError:
logger.error("Connection error: server may be down")
raise
except Exception:
print("Error passed back from SteamQuery")
logger.error("Error passed back from SteamQuery")
traceback.print_exc()
raise
14 changes: 9 additions & 5 deletions app/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@
communications, port and IP verification
"""
import traceback
import logging
from ipaddress import ip_address, IPv6Address, IPv4Address

import requests

logger = logging.getLogger(__name__)
logging.basicConfig(filename='network.log', level=logging.INFO)

# Gets the external IP address where the server is running
# this assumes that the outbound IP after NAT and inbound IP
# before NAT are the same IP address.
Expand All @@ -25,11 +29,11 @@ def get_external_ip() -> str:
try:
response = requests.get('https://ifconfig.me/ip', timeout=5)
server_ip = response.content.decode()
print(f'Discovered IP address is {server_ip}')
logger.info(f'Discovered IP address is {server_ip}')
return str(server_ip)

except Exception:
print("External IP could not be found, ifconfig.me may be down or blocked")
logger.error("External IP could not be found, ifconfig.me may be down or blocked")
traceback.print_exc()
raise

Expand All @@ -47,7 +51,7 @@ def valid_ip_address(ipaddress: int) -> int:
return False

except ValueError:
print("IP address is invalid")
logger.error("IP address is invalid")
traceback.print_exc()
raise

Expand All @@ -61,11 +65,11 @@ def valid_port(port: int) -> bool:
try:
port = int(port)
if port > 0 and port <= 65535:
print(f'PORT {port} is valid')
logger.info(f'PORT {port} is valid')
return True
raise ValueError(f'PORT {port} is not in valid range 1-65535')

except Exception:
print("PORT is not valid")
logger.warning("PORT is not valid")
traceback.print_exc()
raise
7 changes: 7 additions & 0 deletions app/servers.json → app/servers.json.example
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,12 @@
"password": "",
"port": 10308,
"server_type": "DCS"
},
{
"ip_address": "minecraft.example.com",
"name": "Minecraft Server",
"password": "",
"port": 25565,
"server_type": "MINECRAFT_JAVA"
}
]

0 comments on commit c4250d4

Please sign in to comment.