Compare commits
7 Commits
main
...
26ce03cdf6
| Author | SHA1 | Date | |
|---|---|---|---|
| 26ce03cdf6 | |||
| a791803aec | |||
| ef3c2fc1d3 | |||
| 20c2e1ae3c | |||
| 1b897a70a9 | |||
| 7bada37794 | |||
| d7c3cc3cd9 |
34
.dockerignore
Normal file
34
.dockerignore
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# Include any files or directories that you don't want to be copied to your
|
||||||
|
# container here (e.g., local build artifacts, temporary files, etc.).
|
||||||
|
#
|
||||||
|
# For more help, visit the .dockerignore file reference guide at
|
||||||
|
# https://docs.docker.com/go/build-context-dockerignore/
|
||||||
|
|
||||||
|
**/.DS_Store
|
||||||
|
**/__pycache__
|
||||||
|
**/.venv
|
||||||
|
**/.classpath
|
||||||
|
**/.dockerignore
|
||||||
|
**/.env
|
||||||
|
**/.git
|
||||||
|
**/.gitignore
|
||||||
|
**/.project
|
||||||
|
**/.settings
|
||||||
|
**/.toolstarget
|
||||||
|
**/.vs
|
||||||
|
**/.vscode
|
||||||
|
**/*.*proj.user
|
||||||
|
**/*.dbmdl
|
||||||
|
**/*.jfm
|
||||||
|
**/bin
|
||||||
|
**/charts
|
||||||
|
**/docker-compose*
|
||||||
|
**/compose.y*ml
|
||||||
|
**/Dockerfile*
|
||||||
|
**/node_modules
|
||||||
|
**/npm-debug.log
|
||||||
|
**/obj
|
||||||
|
**/secrets.dev.yaml
|
||||||
|
**/values.dev.yaml
|
||||||
|
LICENSE
|
||||||
|
README.md
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1 +1,4 @@
|
|||||||
credentials.json
|
credentials.json
|
||||||
|
\venv\
|
||||||
|
*.env
|
||||||
|
\__pycache__\
|
||||||
51
Dockerfile
Normal file
51
Dockerfile
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
|
# Comments are provided throughout this file to help you get started.
|
||||||
|
# If you need more help, visit the Dockerfile reference guide at
|
||||||
|
# https://docs.docker.com/go/dockerfile-reference/
|
||||||
|
|
||||||
|
# Want to help us make this template better? Share your feedback here: https://forms.gle/ybq9Krt8jtBL3iCk7
|
||||||
|
|
||||||
|
ARG PYTHON_VERSION=3.11.9
|
||||||
|
FROM python:${PYTHON_VERSION}-slim as base
|
||||||
|
|
||||||
|
# Prevents Python from writing pyc files.
|
||||||
|
ENV PYTHONDONTWRITEBYTECODE=1
|
||||||
|
|
||||||
|
# Keeps Python from buffering stdout and stderr to avoid situations where
|
||||||
|
# the application crashes without emitting any logs due to buffering.
|
||||||
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Create a non-privileged user that the app will run under.
|
||||||
|
# See https://docs.docker.com/go/dockerfile-user-best-practices/
|
||||||
|
ARG UID=10001
|
||||||
|
RUN adduser \
|
||||||
|
--disabled-password \
|
||||||
|
--gecos "" \
|
||||||
|
--home "/nonexistent" \
|
||||||
|
--shell "/sbin/nologin" \
|
||||||
|
--no-create-home \
|
||||||
|
--uid "${UID}" \
|
||||||
|
appuser
|
||||||
|
|
||||||
|
# Download dependencies as a separate step to take advantage of Docker's caching.
|
||||||
|
# Leverage a cache mount to /root/.cache/pip to speed up subsequent builds.
|
||||||
|
# Leverage a bind mount to requirements.txt to avoid having to copy them into
|
||||||
|
# into this layer.
|
||||||
|
RUN --mount=type=cache,target=/root/.cache/pip \
|
||||||
|
--mount=type=bind,source=requirements.txt,target=requirements.txt \
|
||||||
|
python -m pip install -r requirements.txt
|
||||||
|
|
||||||
|
# Switch to the non-privileged user to run the application.
|
||||||
|
USER appuser
|
||||||
|
|
||||||
|
# Copy the source code into the container.
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Expose the port that the application listens on.
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
# Run the application.
|
||||||
|
CMD python main.py
|
||||||
22
README.Docker.md
Normal file
22
README.Docker.md
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
### Building and running your application
|
||||||
|
|
||||||
|
When you're ready, start your application by running:
|
||||||
|
`docker compose up --build`.
|
||||||
|
|
||||||
|
Your application will be available at http://localhost:8000.
|
||||||
|
|
||||||
|
### Deploying your application to the cloud
|
||||||
|
|
||||||
|
First, build your image, e.g.: `docker build -t myapp .`.
|
||||||
|
If your cloud uses a different CPU architecture than your development
|
||||||
|
machine (e.g., you are on a Mac M1 and your cloud provider is amd64),
|
||||||
|
you'll want to build the image for that platform, e.g.:
|
||||||
|
`docker build --platform=linux/amd64 -t myapp .`.
|
||||||
|
|
||||||
|
Then, push it to your registry, e.g. `docker push myregistry.com/myapp`.
|
||||||
|
|
||||||
|
Consult Docker's [getting started](https://docs.docker.com/go/get-started-sharing/)
|
||||||
|
docs for more detail on building and pushing.
|
||||||
|
|
||||||
|
### References
|
||||||
|
* [Docker's Python guide](https://docs.docker.com/language/python/)
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
|
## This is the API Branch of this project.
|
||||||
|
The main goal is to provide an RCON backend that can be used by another container (Werbserver)
|
||||||
|
|
||||||
## DayZ Server Management Tool
|
## DayZ Server Management Tool
|
||||||
|
|
||||||
This project is a Python tool for managing a DayZ server using the BattlEye RCON protocol. Key features include:
|
This project is a Python tool for managing a DayZ server using the BattlEye RCON protocol. Key features include:
|
||||||
|
|||||||
BIN
__pycache__/main.cpython-312.pyc
Normal file
BIN
__pycache__/main.cpython-312.pyc
Normal file
Binary file not shown.
132
api_OLREF.py
Normal file
132
api_OLREF.py
Normal 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())
|
||||||
49
compose.yaml
Normal file
49
compose.yaml
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# Comments are provided throughout this file to help you get started.
|
||||||
|
# If you need more help, visit the Docker Compose reference guide at
|
||||||
|
# https://docs.docker.com/go/compose-spec-reference/
|
||||||
|
|
||||||
|
# Here the instructions define your application as a service called "server".
|
||||||
|
# This service is built from the Dockerfile in the current directory.
|
||||||
|
# You can add other services your application may depend on here, such as a
|
||||||
|
# database or a cache. For examples, see the Awesome Compose repository:
|
||||||
|
# https://github.com/docker/awesome-compose
|
||||||
|
services:
|
||||||
|
server:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
ports:
|
||||||
|
- 8000:8000
|
||||||
|
|
||||||
|
# The commented out section below is an example of how to define a PostgreSQL
|
||||||
|
# database that your application can use. `depends_on` tells Docker Compose to
|
||||||
|
# start the database before your application. The `db-data` volume persists the
|
||||||
|
# database data between container restarts. The `db-password` secret is used
|
||||||
|
# to set the database password. You must create `db/password.txt` and add
|
||||||
|
# a password of your choosing to it before running `docker compose up`.
|
||||||
|
# depends_on:
|
||||||
|
# db:
|
||||||
|
# condition: service_healthy
|
||||||
|
# db:
|
||||||
|
# image: postgres
|
||||||
|
# restart: always
|
||||||
|
# user: postgres
|
||||||
|
# secrets:
|
||||||
|
# - db-password
|
||||||
|
# volumes:
|
||||||
|
# - db-data:/var/lib/postgresql/data
|
||||||
|
# environment:
|
||||||
|
# - POSTGRES_DB=example
|
||||||
|
# - POSTGRES_PASSWORD_FILE=/run/secrets/db-password
|
||||||
|
# expose:
|
||||||
|
# - 5432
|
||||||
|
# healthcheck:
|
||||||
|
# test: [ "CMD", "pg_isready" ]
|
||||||
|
# interval: 10s
|
||||||
|
# timeout: 5s
|
||||||
|
# retries: 5
|
||||||
|
# volumes:
|
||||||
|
# db-data:
|
||||||
|
# secrets:
|
||||||
|
# db-password:
|
||||||
|
# file: db/password.txt
|
||||||
|
|
||||||
385
main.py
385
main.py
@@ -1,296 +1,139 @@
|
|||||||
from rcon.battleye import Client
|
from fastapi import FastAPI
|
||||||
from rcon.battleye import Client, ServerMessage
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import json
|
import json
|
||||||
import socket
|
import os
|
||||||
from time import sleep
|
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
|
# Initialize FastAPI app
|
||||||
SERVER_ADDRESS = ''
|
app = FastAPI()
|
||||||
SERVER_PORT = 0
|
|
||||||
RCON_PASSWORD = ''
|
|
||||||
|
|
||||||
# Check if credentials file exists
|
# Add CORS middleware
|
||||||
if os.path.exists('credentials.json'):
|
app.add_middleware(
|
||||||
# Load credentials from file
|
CORSMiddleware,
|
||||||
with open('credentials.json', 'r') as file:
|
allow_origins=["*"], # Allow all origins
|
||||||
credentials = json.load(file)
|
allow_credentials=True,
|
||||||
SERVER_ADDRESS = credentials.get('SERVER_ADDRESS', '')
|
allow_methods=["*"], # Allow all methods
|
||||||
SERVER_PORT = credentials.get('SERVER_PORT', 0)
|
allow_headers=["*"], # Allow all headers
|
||||||
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()
|
|
||||||
|
|
||||||
|
# Initialize RCON client
|
||||||
|
client = rcon.AsyncRCONClient()
|
||||||
|
|
||||||
def print_menu():
|
# Message store
|
||||||
print()
|
messages = []
|
||||||
print("1. List Players")
|
player_connect_messages = []
|
||||||
print("2. Messaging")
|
|
||||||
print("3. Locking")
|
|
||||||
print("4. Kicking")
|
|
||||||
print("5. Banning")
|
|
||||||
print("6. Server menu")
|
|
||||||
print("7. Exit")
|
|
||||||
|
|
||||||
def handle_input(choice):
|
# Event handler for player connect messages
|
||||||
if choice == "1":
|
@client.dispatch.on_message
|
||||||
list_players()
|
async def on_player_connect(player_connect_message: str):
|
||||||
elif choice == "2":
|
print('Player connected:', player_connect_message)
|
||||||
print_message_menu()
|
player_connect_messages.append(player_connect_message)
|
||||||
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.")
|
|
||||||
|
|
||||||
def print_message_menu():
|
@client.dispatch.on_command
|
||||||
print("1. Send Global Message")
|
async def server_response_to_command(response: str):
|
||||||
print("2. Send Direct Message")
|
print('Command response:', response)
|
||||||
print("3. Back")
|
messages.append(response)
|
||||||
|
|
||||||
def print_server_menu():
|
# Background task to run the RCON client
|
||||||
print("1. Soft Server Restart (Restart within 3 minutes with warning messages sent to the players)")
|
async def run_rcon_client():
|
||||||
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():
|
|
||||||
try:
|
try:
|
||||||
with Client(SERVER_ADDRESS, SERVER_PORT, passwd=RCON_PASSWORD) as client:
|
async with client.connect(IP_ADDR, PORT, PASSWORD):
|
||||||
response = client.run('players')
|
while True:
|
||||||
print(response)
|
await asyncio.sleep(60) # Sleep for an hour and then check the connection
|
||||||
except socket.timeout:
|
|
||||||
print("Connection timed out. Please check the server address, port, and network settings.")
|
|
||||||
except Exception as e:
|
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():
|
# API endpoint to get messages
|
||||||
list_players()
|
@app.get("/loginmessages", response_class=JSONResponse)
|
||||||
player_name = input("Enter the # (number) of the player to kick: ")
|
async def get_messages() -> List[str]:
|
||||||
reason = input("Enter the reason for kicking the player (can be empty): ")
|
return player_connect_messages
|
||||||
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)
|
|
||||||
|
|
||||||
def kick_all_players():
|
# API endpoint to get players list
|
||||||
with Client(SERVER_ADDRESS, SERVER_PORT, passwd=RCON_PASSWORD) as client:
|
@app.get("/players")
|
||||||
response = client.run('#kick -1')
|
async def get_players() -> List[Dict[str, Any]]:
|
||||||
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():
|
|
||||||
try:
|
try:
|
||||||
with Client(SERVER_ADDRESS, SERVER_PORT, passwd=RCON_PASSWORD) as client:
|
players = await client.fetch_players()
|
||||||
response = client.run('#lock')
|
player_list = [{"id": player.id, "name": player.name} for player in players]
|
||||||
print(response)
|
return JSONResponse(content=player_list)
|
||||||
except socket.timeout:
|
|
||||||
print("Connection timed out. Please check the server address, port, and network settings.")
|
|
||||||
except Exception as e:
|
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:
|
try:
|
||||||
with Client(SERVER_ADDRESS, SERVER_PORT, passwd=RCON_PASSWORD) as client:
|
response = await client.kick(player_id, reason)
|
||||||
response = client.run('#unlock')
|
return JSONResponse(content={"message": response})
|
||||||
print(response)
|
|
||||||
except socket.timeout:
|
|
||||||
print("Connection timed out. Please check the server address, port, and network settings.")
|
|
||||||
except Exception as e:
|
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:
|
try:
|
||||||
with Client(SERVER_ADDRESS, SERVER_PORT, passwd=RCON_PASSWORD) as client:
|
response = await client.ban(addr, duration, reason)
|
||||||
response = client.run('players')
|
return JSONResponse(content={"message": response})
|
||||||
print(response)
|
|
||||||
except socket.timeout:
|
|
||||||
print("Connection timed out. Please check the server address, port, and network settings.")
|
|
||||||
except Exception as e:
|
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():
|
# API endpoint to send a global message
|
||||||
while True:
|
@app.post("/message")
|
||||||
print_menu()
|
async def send_global_message(message: str):
|
||||||
choice = input("Enter your choice: ")
|
try:
|
||||||
handle_input(choice)
|
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__":
|
if __name__ == "__main__":
|
||||||
create_cli()
|
import uvicorn
|
||||||
|
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||||
|
|||||||
@@ -1 +1,4 @@
|
|||||||
rcon
|
fastapi
|
||||||
|
uvicorn[standard]
|
||||||
|
berconpy
|
||||||
|
pydantic
|
||||||
98
tester.html
Normal file
98
tester.html
Normal 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>
|
||||||
Reference in New Issue
Block a user