Compare commits

..

5 Commits

7 changed files with 355 additions and 273 deletions

3
.gitignore vendored
View File

@@ -1 +1,4 @@
credentials.json
\venv\
*.env
\__pycache__\

View File

@@ -1,3 +1,6 @@
## This is the API Branch of this project.
The mail goal is to provide an RCON backend that can be used by another container (Werbserver)
## DayZ Server Management Tool
This project is a Python tool for managing a DayZ server using the BattlEye RCON protocol. Key features include:

Binary file not shown.

132
api_OLREF.py Normal file
View File

@@ -0,0 +1,132 @@
import asyncio
import logging
import uvicorn
import json
import os
from pydantic import BaseModel
import berconpy as rcon
from fastapi import FastAPI, HTTPException, Depends
# RCON Server Details
credentials_file = os.path.join(os.path.dirname(__file__), "credentials.json")
with open(credentials_file) as f:
credentials = json.load(f)
IP_ADDR = credentials["SERVER_ADDRESS"]
PORT = credentials["SERVER_PORT"]
PASSWORD = credentials["RCON_PASSWORD"]
# Logging Configuration
log = logging.getLogger("berconpy")
log.setLevel(logging.DEBUG)
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter("%(asctime)s:%(levelname)s:%(name)s: %(message)s"))
log.addHandler(handler)
# RCON Client Initialization
client = rcon.AsyncRCONClient()
# FastAPI Initialization
app = FastAPI()
# Player Model
class Player(BaseModel):
name: str
score: int
class PlayerAction(BaseModel):
playerid: str
reason: str
duration: int = None # Optional for ban duration
# Dependency to get RCON manager
async def get_rcon_manager():
async with client.connect(IP_ADDR, PORT, PASSWORD):
yield client
# FastAPI Endpoint to List Players
@app.get("/players")
async def list_players(rcon: rcon.AsyncRCONClient = Depends(get_rcon_manager)):
response = await rcon.fetch_players()
return {"response": response}
# Kick Player Endpoint
@app.post("/kick")
async def kick_player(action: PlayerAction, rcon: rcon.AsyncRCONClient = Depends(get_rcon_manager)):
try:
response = await rcon.kick(f'{action.playerid} {action.reason}')
return {"response": response}
except Exception as e:
log.error(f"Error kicking player: {e}")
raise HTTPException(status_code=500, detail="Error kicking player")
# Ban Player Endpoint
@app.post("/ban")
async def ban_player(action: PlayerAction, rcon: rcon.AsyncRCONClient = Depends(get_rcon_manager)):
try:
duration_str = f"{action.duration} " if action.duration else ""
response = await rcon.ban(f'{action.playerid} {duration_str}"{action.reason}"')
return {"response": response}
except Exception as e:
log.error(f"Error banning player: {e}")
raise HTTPException(status_code=500, detail="Error banning player")
# Unlock Server Endpoint
@app.post("/unlock")
async def unlock_server(rcon: rcon.AsyncRCONClient = Depends(get_rcon_manager)):
try:
response = await rcon.send_command('#unlock')
return {"response": response}
except Exception as e:
log.error(f"Error unlocking server: {e}")
raise HTTPException(status_code=500, detail="Error unlocking server")
# Global Message Endpoint
@app.post("/global_message")
async def global_message(message: str, rcon: rcon.AsyncRCONClient = Depends(get_rcon_manager)):
try:
response = await rcon.send(f'{message}')
return {"response": response}
except Exception as e:
log.error(f"Error sending global message: {e}")
raise HTTPException(status_code=500, detail="Error sending global message")
# Direct Message Endpoint
@app.post("/direct_message")
async def direct_message(playerid: str, message: str, rcon: rcon.AsyncRCONClient = Depends(get_rcon_manager)):
try:
response = await rcon.whisper(f'{playerid} {message}')
return {"response": response}
except Exception as e:
log.error(f"Error sending direct message: {e}")
raise HTTPException(status_code=500, detail="Error sending direct message")
# Lock Server Endpoint
@app.post("/lock")
async def lock_server(rcon: rcon.AsyncRCONClient = Depends(get_rcon_manager)):
try:
response = await rcon.send_command('#lock')
return {"response": response}
except Exception as e:
log.error(f"Error locking server: {e}")
raise HTTPException(status_code=500, detail="Error locking server")
# RCON Event Handlers
@client.dispatch.on_login
async def on_login():
print("Connected to RCON server.")
@client.dispatch.on_message
async def on_message(message: str):
print("Received message from server:", message)
@client.dispatch.on_command
async def server_response_to_command(response: str):
if not response:
return print("on_command: <empty>")
print("on_command:", response)
# Main Function to Run FastAPI App
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
asyncio.run(get_rcon_manager())

385
main.py
View File

@@ -1,296 +1,139 @@
from rcon.battleye import Client
from rcon.battleye import Client, ServerMessage
import sys
import os
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import json
import socket
from time import sleep
import os
import asyncio
from typing import List, Dict, Any
from fastapi.responses import JSONResponse
import berconpy as rcon
# RCON Server Details
credentials_file = os.path.join(os.path.dirname(__file__), "credentials.json")
try:
with open(credentials_file) as f:
credentials = json.load(f)
except FileNotFoundError:
raise Exception(f"Credentials file not found: {credentials_file}")
except json.JSONDecodeError:
raise Exception(f"Error decoding credentials file: {credentials_file}")
IP_ADDR = credentials["SERVER_ADDRESS"]
PORT = credentials["SERVER_PORT"]
PASSWORD = credentials["RCON_PASSWORD"]
# Define the server details
SERVER_ADDRESS = ''
SERVER_PORT = 0
RCON_PASSWORD = ''
# Initialize FastAPI app
app = FastAPI()
# Check if credentials file exists
if os.path.exists('credentials.json'):
# Load credentials from file
with open('credentials.json', 'r') as file:
credentials = json.load(file)
SERVER_ADDRESS = credentials.get('SERVER_ADDRESS', '')
SERVER_PORT = credentials.get('SERVER_PORT', 0)
RCON_PASSWORD = credentials.get('RCON_PASSWORD', '')
else:
# Generate example credentials file
credentials = {
'SERVER_ADDRESS': 'YOUR-SERVER-ADDRESS', # Example Values
'SERVER_PORT': 2303, # Example Values
'RCON_PASSWORD': 'RCON_PASSWORD' # Example Values
}
with open('credentials.json', 'w') as file:
json.dump(credentials, file, indent=4)
print("Please fill in the server details in the credentials.json file.")
sys.exit()
# Add CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Allow all origins
allow_credentials=True,
allow_methods=["*"], # Allow all methods
allow_headers=["*"], # Allow all headers
)
# Initialize RCON client
client = rcon.AsyncRCONClient()
def print_menu():
print()
print("1. List Players")
print("2. Messaging")
print("3. Locking")
print("4. Kicking")
print("5. Banning")
print("6. Server menu")
print("7. Exit")
# Message store
messages = []
player_connect_messages = []
def handle_input(choice):
if choice == "1":
list_players()
elif choice == "2":
print_message_menu()
message_choice = input("Enter your choice: ")
handle_message_input(message_choice)
elif choice == "3":
print_locking_menu()
locking_choice = input("Enter your choice: ")
handle_locking_input(locking_choice)
elif choice == "4":
print_kick_menu()
kick_choice = input("Enter your choice: ")
handle_kick_input(kick_choice)
elif choice == "5":
print_banning_menu()
banning_choice = input("Enter your choice: ")
handle_banning_input(banning_choice)
elif choice == "6":
print_server_menu()
server_choice = input("Enter your choice: ")
handle_server_input(server_choice)
elif choice == "7":
sys.exit()
else:
print("Invalid choice. Please try again.")
# Event handler for player connect messages
@client.dispatch.on_message
async def on_player_connect(player_connect_message: str):
print('Player connected:', player_connect_message)
player_connect_messages.append(player_connect_message)
def print_message_menu():
print("1. Send Global Message")
print("2. Send Direct Message")
print("3. Back")
@client.dispatch.on_command
async def server_response_to_command(response: str):
print('Command response:', response)
messages.append(response)
def print_server_menu():
print("1. Soft Server Restart (Restart within 3 minutes with warning messages sent to the players)")
print("2. Hard Server Restart (Restart immediately without warning messages)")
print("3. Back")
def print_locking_menu():
print("1. Lock Server")
print("2. Unlock Server")
print("3. Back")
def print_kick_menu():
print("1. Kick Single Player")
print("2. Kick All Players")
print("3. Back")
def print_banning_menu():
print("1. Show Banlist")
print("2. Ban Player")
print("3. Ban Player by SteamID")
print("4. Unban Player")
print("5. Back")
def handle_message_input(choice):
if choice == "1":
send_global_message()
elif choice == "2":
send_direct_message()
elif choice == "3":
print_menu()
else:
print("Invalid choice. Please try again.")
def handle_locking_input(choice):
if choice == "1":
lock_server()
elif choice == "2":
unlock_server()
elif choice == "3":
print_menu()
else:
print("Invalid choice. Please try again.")
def handle_kick_input(choice):
if choice == "1":
kick_single_player()
elif choice == "2":
kick_all_players()
elif choice == "3":
print_menu()
else:
print("Invalid choice. Please try again.")
def handle_banning_input(choice):
if choice == "1":
show_banlist()
elif choice == "2":
ban_player()
elif choice == "3":
ban_player_by_steamid()
elif choice == "4":
unban_player()
elif choice == "5":
print_menu()
else:
print("Invalid choice. Please try again.")
def handle_server_input(choice):
if choice == "1":
soft_restart()
elif choice == "2":
confirm = input("Are you sure you want to perform a hard restart? (y/n): ")
if confirm.lower() == "y":
hard_restart()
else:
print("Hard restart cancelled.")
elif choice == "3":
print_menu()
else:
print("Invalid choice. Please try again.")
def send_global_message():
message = input("Enter the message to send: ")
with Client(SERVER_ADDRESS, SERVER_PORT, passwd=RCON_PASSWORD) as client:
response = client.run(f'Say -1 {message}')
print(response)
def send_direct_message():
list_players()
player_name = input("Enter the # (number) of the player to message: ")
message = input("Enter the message to send: ")
with Client(SERVER_ADDRESS, SERVER_PORT, passwd=RCON_PASSWORD) as client:
response = client.run(f'Say -{player_name} {message}')
print(response)
def hard_restart():
with Client(SERVER_ADDRESS, SERVER_PORT, passwd=RCON_PASSWORD) as client:
response = client.run('#shutdown')
print(response)
def soft_restart(): # Not implemented yet
print('not implemented yet')
pass
def list_players():
# Background task to run the RCON client
async def run_rcon_client():
try:
with Client(SERVER_ADDRESS, SERVER_PORT, passwd=RCON_PASSWORD) as client:
response = client.run('players')
print(response)
except socket.timeout:
print("Connection timed out. Please check the server address, port, and network settings.")
async with client.connect(IP_ADDR, PORT, PASSWORD):
while True:
await asyncio.sleep(3600) # Sleep for an hour and then check the connection
except Exception as e:
print(f"An error occurred: {e}")
print(f"Error in RCON client: {e}")
finally:
await client.close()
def kick_single_player():
list_players()
player_name = input("Enter the # (number) of the player to kick: ")
reason = input("Enter the reason for kicking the player (can be empty): ")
with Client(SERVER_ADDRESS, SERVER_PORT, passwd=RCON_PASSWORD) as client:
response = client.run(f'kick {player_name} "{reason}"')
if not response:
print("Player kicked successfully.")
else:
print(response)
# API endpoint to get messages
@app.get("/loginmessages", response_class=JSONResponse)
async def get_messages() -> List[str]:
return player_connect_messages
def kick_all_players():
with Client(SERVER_ADDRESS, SERVER_PORT, passwd=RCON_PASSWORD) as client:
response = client.run('#kick -1')
print(response)
def show_banlist():
with Client(SERVER_ADDRESS, SERVER_PORT, passwd=RCON_PASSWORD) as client:
response = client.run('Bans')
print(response)
def ban_player():
list_players()
player_name = input("Enter the # (number) of the player to ban: ")
ban_time = input("Enter the ban time in minutes: ")
reason = input("Enter the reason for banning the player (can be empty): ")
with Client(SERVER_ADDRESS, SERVER_PORT, passwd=RCON_PASSWORD) as client:
response = client.run(f'ban {player_name} {ban_time} "{reason}"')
if not response:
print("Player banned successfully.")
else:
print(response)
def server_monitor():
while True:
try:
with Client(SERVER_ADDRESS, SERVER_PORT, passwd=RCON_PASSWORD) as client:
response = client.run('status')
print(response)
except socket.timeout:
print("Connection timed out. Please check the server address, port, and network settings.")
except Exception as e:
print(f"An error occurred: {e}")
sleep(30)
def ban_player_by_steamid():
steamid = input("Enter the SteamID of the player to ban: ")
ban_time = input("Enter the ban time in minutes: ")
reason = input("Enter the reason for banning the player (can be empty): ")
with Client(SERVER_ADDRESS, SERVER_PORT, passwd=RCON_PASSWORD) as client:
response = client.run(f'ban -1 {ban_time} "{reason}" {steamid}')
if not response:
print("Player banned successfully.")
else:
print(response)
def unban_player():
show_banlist()
ban_id = input("Enter the # (number) of the ban to remove: ")
with Client(SERVER_ADDRESS, SERVER_PORT, passwd=RCON_PASSWORD) as client:
response = client.run(f'removeBan {ban_id}')
if not response:
print("Ban removed successfully.")
else:
print(response)
def lock_server():
# API endpoint to get players list
@app.get("/players")
async def get_players() -> List[Dict[str, Any]]:
try:
with Client(SERVER_ADDRESS, SERVER_PORT, passwd=RCON_PASSWORD) as client:
response = client.run('#lock')
print(response)
except socket.timeout:
print("Connection timed out. Please check the server address, port, and network settings.")
players = await client.fetch_players()
player_list = [{"id": player.id, "name": player.name} for player in players]
return JSONResponse(content=player_list)
except Exception as e:
print(f"An error occurred: {e}")
print(f"Error fetching players: {e}")
return JSONResponse(status_code=500, content={"message": "Error fetching players"})
def unlock_server():
# API endpoint to kick a player
@app.post("/kick")
async def kick_player(player_id: int, reason: str = ""):
try:
with Client(SERVER_ADDRESS, SERVER_PORT, passwd=RCON_PASSWORD) as client:
response = client.run('#unlock')
print(response)
except socket.timeout:
print("Connection timed out. Please check the server address, port, and network settings.")
response = await client.kick(player_id, reason)
return JSONResponse(content={"message": response})
except Exception as e:
print(f"An error occurred: {e}")
print(f"Error kicking player: {e}")
return JSONResponse(status_code=500, content={"message": "Error kicking player"})
def list_players():
# API endpoint to ban a player
@app.post("/ban")
async def ban_player(addr: str, duration: int = 0, reason: str = ""):
try:
with Client(SERVER_ADDRESS, SERVER_PORT, passwd=RCON_PASSWORD) as client:
response = client.run('players')
print(response)
except socket.timeout:
print("Connection timed out. Please check the server address, port, and network settings.")
response = await client.ban(addr, duration, reason)
return JSONResponse(content={"message": response})
except Exception as e:
print(f"An error occurred: {e}")
print(f"Error banning player: {e}")
return JSONResponse(status_code=500, content={"message": "Error banning player"})
def create_cli():
while True:
print_menu()
choice = input("Enter your choice: ")
handle_input(choice)
# API endpoint to send a global message
@app.post("/message")
async def send_global_message(message: str):
try:
response = await client.send(message)
return JSONResponse(content={"message": response})
except Exception as e:
print(f"Error sending message: {e}")
return JSONResponse(status_code=500, content={"message": "Error sending message"})
# API endpoint to unban a player
@app.post("/unban")
async def unban_player(ban_id: int):
try:
response = await client.unban(ban_id)
return JSONResponse(content={"message": response})
except Exception as e:
print(f"Error unbanning player: {e}")
return JSONResponse(status_code=500, content={"message": "Error unbanning player"})
# API endpoint to send a private message (whisper) to a player
@app.post("/whisper")
async def whisper_player(player_id: int, message: str):
try:
response = await client.whisper(player_id, message)
return JSONResponse(content={"message": response})
except Exception as e:
print(f"Error sending whisper: {e}")
return JSONResponse(status_code=500, content={"message": "Error sending whisper"})
# Start the RCON client in the background when the app starts
@app.on_event("startup")
async def startup_event():
asyncio.create_task(run_rcon_client())
# Main entry point for running the FastAPI app
if __name__ == "__main__":
create_cli()
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)

View File

@@ -1 +1,4 @@
rcon
fastapi
uvicorn[standard]
berconpy
pydantic

98
tester.html Normal file
View File

@@ -0,0 +1,98 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Player Connect Messages</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
}
#messages {
border: 1px solid #ccc;
padding: 10px;
height: 400px;
overflow-y: scroll;
}
.message {
border-bottom: 1px solid #eee;
padding: 5px 0;
}
#player-count {
font-size: 20px;
margin-bottom: 20px;
}
</style>
</head>
<body>
<h1>Player Connect Messages</h1>
<div id="player-count">Current Players: 0</div>
<div id="messages"></div>
<script>
function fetchMessages() {
const xhr = new XMLHttpRequest();
xhr.open('GET', 'http://localhost:8000/loginmessages', true);
xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
const data = JSON.parse(xhr.responseText);
console.log('Fetched messages:', data);
updateMessages(data);
} else {
console.error('Error fetching messages:', xhr.statusText);
}
};
xhr.onerror = function() {
console.error('Network error while fetching messages');
};
xhr.send();
}
function fetchPlayerCount() {
const xhr = new XMLHttpRequest();
xhr.open('GET', 'http://localhost:8000/players', true);
xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
const data = JSON.parse(xhr.responseText);
console.log('Fetched player count:', data.length);
updatePlayerCount(data.length);
} else {
console.error('Error fetching player count:', xhr.statusText);
}
};
xhr.onerror = function() {
console.error('Network error while fetching player count');
};
xhr.send();
}
function updateMessages(messages) {
const messagesDiv = document.getElementById('messages');
messagesDiv.innerHTML = '';
messages.forEach(message => {
const messageDiv = document.createElement('div');
messageDiv.className = 'message';
messageDiv.textContent = message;
messagesDiv.appendChild(messageDiv);
});
console.log('Updated messages displayed');
}
function updatePlayerCount(count) {
const playerCountDiv = document.getElementById('player-count');
playerCountDiv.textContent = `Current Players: ${count}`;
}
// Fetch messages every 5 seconds
setInterval(fetchMessages, 5000);
// Fetch player count every 5 seconds
setInterval(fetchPlayerCount, 5000);
// Initial fetch
fetchMessages();
fetchPlayerCount();
</script>
</body>
</html>