Update 2025-04-13_16:49:18
This commit is contained in:
139
main.py
Normal file
139
main.py
Normal file
@ -0,0 +1,139 @@
|
||||
from fastapi import FastAPI, Request, Body
|
||||
from fastapi.responses import JSONResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
import requests, random, logging, json
|
||||
from logging.handlers import RotatingFileHandler
|
||||
from fastapi.responses import HTMLResponse
|
||||
from pathlib import Path
|
||||
|
||||
log_path = "/var/log/games/WordCraze.log"
|
||||
logger = logging.getLogger("WordCraze")
|
||||
logger.setLevel(logging.INFO)
|
||||
handler = RotatingFileHandler(log_path, maxBytes=1_000_000, backupCount=3)
|
||||
formatter = logging.Formatter('%(asctime)s - %(message)s')
|
||||
handler.setFormatter(formatter)
|
||||
logger.addHandler(handler)
|
||||
|
||||
app = FastAPI()
|
||||
app.mount("/static", StaticFiles(directory="static", html=True), name="static")
|
||||
|
||||
@app.get("/", response_class=HTMLResponse)
|
||||
async def root():
|
||||
return Path("static/index.html").read_text(encoding="utf-8")
|
||||
|
||||
class GameState:
|
||||
def __init__(self, word: str):
|
||||
self.word = word
|
||||
self.guessed = ["_"] * len(word)
|
||||
self.attempts = 8
|
||||
self.status = "playing"
|
||||
self.correct_total = 0
|
||||
|
||||
state = None
|
||||
|
||||
def getWordsFromOllama():
|
||||
try:
|
||||
url = "https://at1.dynproxy.net/api/chat/completions"
|
||||
headers = {"Authorization": "Bearer sk-d0e3a491b19c435a975b234969298cd0"}
|
||||
body = {
|
||||
"model": "gemma3:1b",
|
||||
"messages": [
|
||||
{
|
||||
"role": "system",
|
||||
"content": "Gib ein zufälliges Thema an (z. B. Tiere, Essen, Möbel) und dann ein Array mit 5 Wörtern dieses Themas mit 3 bis 6 Buchstaben im JSON-Format. Nur das Array, keine Einleitung."
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Einfaches Thema und 5 passende Wörter mit 3–6 Buchstaben im JSON-Array."
|
||||
}
|
||||
],
|
||||
"stream": False
|
||||
}
|
||||
r = requests.post(url, json=body, headers=headers, timeout=5)
|
||||
r.raise_for_status()
|
||||
content = r.json().get("choices", [{}])[0].get("message", {}).get("content", "[]")
|
||||
clean = content.strip()
|
||||
if clean.startswith("```json"):
|
||||
clean = clean.removeprefix("```json").strip()
|
||||
if clean.endswith("```"):
|
||||
clean = clean.removesuffix("```").strip()
|
||||
logger.info(f"Ollama-Rohantwort:\n{clean}")
|
||||
return json.loads(clean)
|
||||
except Exception as e:
|
||||
logger.warning(f"Fallback Ollama getWordsFromOllama(): {e}")
|
||||
return []
|
||||
|
||||
@app.post("/api/start")
|
||||
def start_game():
|
||||
global state
|
||||
words = getWordsFromOllama()
|
||||
if not words:
|
||||
return JSONResponse({"error": "No words found"}, status_code=500)
|
||||
word = random.choice(words)
|
||||
state = GameState(word)
|
||||
|
||||
try:
|
||||
desc_req = requests.post(
|
||||
"https://at1.dynproxy.net/api/chat/completions",
|
||||
headers={"Authorization": "Bearer sk-d0e3a491b19c435a975b234969298cd0"},
|
||||
json={
|
||||
"model": "gemma3:1b",
|
||||
"messages": [
|
||||
{"role": "user", "content": f"Nenne ein Oberthema (wie ‚Möbel‘, ‚Tiere‘, ‚Sport‘), zu dem das Wort ‚{word}‘ gehört. Antworte nur mit einem allgemeinen Wort. Nenne niemals das Wort selbst."}
|
||||
],
|
||||
"stream": False
|
||||
},
|
||||
timeout=5
|
||||
)
|
||||
desc_req.raise_for_status()
|
||||
desc = desc_req.json().get("choices", [{}])[0].get("message", {}).get("content", "(unbekannt)").strip()
|
||||
except Exception as e:
|
||||
logger.warning(f"Beschreibung für '{word}' fehlgeschlagen: {e}")
|
||||
desc = "(unbekannt)"
|
||||
|
||||
logger.info(f"Zufallsstart mit Wort: {word}")
|
||||
return {"word": state.guessed, "attempts": state.attempts, "source": "ollama", "topic": desc}
|
||||
|
||||
@app.post("/api/guess/{letter}")
|
||||
def guess_letter(letter: str):
|
||||
global state
|
||||
if not state or state.status != "playing":
|
||||
return {"status": "not_started"}
|
||||
letter = letter.lower()
|
||||
correct_this_turn = 0
|
||||
for i, char in enumerate(state.word.lower()):
|
||||
if char == letter and state.guessed[i] == "_":
|
||||
state.guessed[i] = state.word[i]
|
||||
correct_this_turn += 1
|
||||
state.correct_total += correct_this_turn
|
||||
if "_" not in state.guessed:
|
||||
state.status = "won"
|
||||
elif correct_this_turn == 0:
|
||||
state.attempts -= 1
|
||||
if state.attempts <= 0:
|
||||
state.status = "lost"
|
||||
response = {
|
||||
"word": state.guessed,
|
||||
"attempts": state.attempts,
|
||||
"correct_this_turn": correct_this_turn,
|
||||
"total_correct": state.correct_total,
|
||||
"status": state.status
|
||||
}
|
||||
|
||||
if state.status != "playing":
|
||||
response["target"] = state.word
|
||||
|
||||
return response
|
||||
|
||||
@app.post("/api/moderate")
|
||||
async def moderate(request: Request):
|
||||
data = await request.json()
|
||||
text = data.get("text", "")
|
||||
bad = any(w in text.lower() for w in ["shit", "fuck", "spam"])
|
||||
logger.info(f"Moderation checked: '{text}' → {'REJECTED' if bad else 'OK'}")
|
||||
return {"is_ok": not bad}
|
||||
|
||||
@app.get("/api/ping")
|
||||
def ping():
|
||||
return {"status": "ok"}
|
||||
|
Reference in New Issue
Block a user