Updating event listeners and API to use the new library

This commit is contained in:
2024-06-14 03:30:10 +02:00
parent 20c2e1ae3c
commit ef3c2fc1d3
4 changed files with 244 additions and 72 deletions

View File

@@ -47,29 +47,14 @@ async def get_rcon_manager():
# FastAPI Endpoint to List Players # FastAPI Endpoint to List Players
@app.get("/players") @app.get("/players")
async def list_players(rcon: rcon.AsyncRCONClient = Depends(get_rcon_manager)): async def list_players(rcon: rcon.AsyncRCONClient = Depends(get_rcon_manager)):
try: response = await rcon.fetch_players()
response = await rcon.send_command("players") return {"response": response}
players = []
for line in response.splitlines():
parts = line.split()
if len(parts) >= 2:
name = parts[0]
try:
score = int(parts[1])
except ValueError:
score = 0
player = Player(name=name, score=score)
players.append(player)
return players
except Exception as e:
log.error(f"Error fetching players: {e}")
raise HTTPException(status_code=500, detail="Error fetching players")
# Kick Player Endpoint # Kick Player Endpoint
@app.post("/kick") @app.post("/kick")
async def kick_player(action: PlayerAction, rcon: rcon.AsyncRCONClient = Depends(get_rcon_manager)): async def kick_player(action: PlayerAction, rcon: rcon.AsyncRCONClient = Depends(get_rcon_manager)):
try: try:
response = await rcon.send_command(f'kick {action.playerid} {action.reason}') response = await rcon.kick(f'{action.playerid} {action.reason}')
return {"response": response} return {"response": response}
except Exception as e: except Exception as e:
log.error(f"Error kicking player: {e}") log.error(f"Error kicking player: {e}")
@@ -80,7 +65,7 @@ async def kick_player(action: PlayerAction, rcon: rcon.AsyncRCONClient = Depends
async def ban_player(action: PlayerAction, rcon: rcon.AsyncRCONClient = Depends(get_rcon_manager)): async def ban_player(action: PlayerAction, rcon: rcon.AsyncRCONClient = Depends(get_rcon_manager)):
try: try:
duration_str = f"{action.duration} " if action.duration else "" duration_str = f"{action.duration} " if action.duration else ""
response = await rcon.send_command(f'ban {action.playerid} {duration_str}"{action.reason}"') response = await rcon.ban(f'{action.playerid} {duration_str}"{action.reason}"')
return {"response": response} return {"response": response}
except Exception as e: except Exception as e:
log.error(f"Error banning player: {e}") log.error(f"Error banning player: {e}")
@@ -100,7 +85,7 @@ async def unlock_server(rcon: rcon.AsyncRCONClient = Depends(get_rcon_manager)):
@app.post("/global_message") @app.post("/global_message")
async def global_message(message: str, rcon: rcon.AsyncRCONClient = Depends(get_rcon_manager)): async def global_message(message: str, rcon: rcon.AsyncRCONClient = Depends(get_rcon_manager)):
try: try:
response = await rcon.send_command(f'Say -1 {message}') response = await rcon.send(f'{message}')
return {"response": response} return {"response": response}
except Exception as e: except Exception as e:
log.error(f"Error sending global message: {e}") log.error(f"Error sending global message: {e}")
@@ -110,7 +95,7 @@ async def global_message(message: str, rcon: rcon.AsyncRCONClient = Depends(get_
@app.post("/direct_message") @app.post("/direct_message")
async def direct_message(playerid: str, message: str, rcon: rcon.AsyncRCONClient = Depends(get_rcon_manager)): async def direct_message(playerid: str, message: str, rcon: rcon.AsyncRCONClient = Depends(get_rcon_manager)):
try: try:
response = await rcon.send_command(f'Say {playerid} {message}') response = await rcon.whisper(f'{playerid} {message}')
return {"response": response} return {"response": response}
except Exception as e: except Exception as e:
log.error(f"Error sending direct message: {e}") log.error(f"Error sending direct message: {e}")
@@ -144,3 +129,4 @@ async def server_response_to_command(response: str):
# Main Function to Run FastAPI App # Main Function to Run FastAPI App
if __name__ == "__main__": if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000) uvicorn.run(app, host="0.0.0.0", port=8000)
asyncio.run(get_rcon_manager())

View File

@@ -1,51 +0,0 @@
"""Listens to an RCON server for messages."""
import asyncio
import logging
import math
import berconpy as rcon
IP = "217.72.204.88"
PORT = 2313
PASSWORD = "u1BSlMM4LH0"
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)
client = rcon.AsyncRCONClient()
@client.dispatch.on_login
async def on_login():
print("on_login")
@client.dispatch.on_message
async def on_message(message: str):
print("on_message:", message)
@client.dispatch.on_command
async def server_response_to_command(response: str):
# this event also includes keep alive commands we send to the server;
# for handling commands, reading the return value of
# `await client.send_command()` is the recommended method
if not response:
return print("on_command: <empty>")
print("on_command:", response)
# Other events are documented in AsyncRCONClient
async def main():
async with client.connect(IP, PORT, PASSWORD):
await asyncio.sleep(math.inf)
if __name__ == "__main__":
asyncio.run(main())

139
main.py Normal file
View File

@@ -0,0 +1,139 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import json
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"]
# Initialize FastAPI app
app = FastAPI()
# 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()
# Message store
messages = []
player_connect_messages = []
# 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)
@client.dispatch.on_command
async def server_response_to_command(response: str):
print('Command response:', response)
messages.append(response)
# Background task to run the RCON client
async def run_rcon_client():
try:
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"Error in RCON client: {e}")
finally:
await client.close()
# API endpoint to get messages
@app.get("/loginmessages", response_class=JSONResponse)
async def get_messages() -> List[str]:
return player_connect_messages
# API endpoint to get players list
@app.get("/players")
async def get_players() -> List[Dict[str, Any]]:
try:
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"Error fetching players: {e}")
return JSONResponse(status_code=500, content={"message": "Error fetching players"})
# API endpoint to kick a player
@app.post("/kick")
async def kick_player(player_id: int, reason: str = ""):
try:
response = await client.kick(player_id, reason)
return JSONResponse(content={"message": response})
except Exception as e:
print(f"Error kicking player: {e}")
return JSONResponse(status_code=500, content={"message": "Error kicking player"})
# API endpoint to ban a player
@app.post("/ban")
async def ban_player(addr: str, duration: int = 0, reason: str = ""):
try:
response = await client.ban(addr, duration, reason)
return JSONResponse(content={"message": response})
except Exception as e:
print(f"Error banning player: {e}")
return JSONResponse(status_code=500, content={"message": "Error banning player"})
# 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__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)

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>