From 8e2df91ed4061c53ee8416f39aae172867383b18 Mon Sep 17 00:00:00 2001 From: extreme4all <> Date: Sat, 15 Aug 2026 14:22:09 +0200 Subject: [PATCH] cleanup --- .../website/api/account_search.py | 42 +-- .../website/app/controllers/bot_detector.py | 66 ++--- .../bot_detector/website/templates/base.html | 6 +- .../bot_detector/website/templates/head.html | 6 +- .../templates/pages/account_search.html | 279 +++++++++++------- docker-compose-dev.yml | 4 +- 6 files changed, 232 insertions(+), 171 deletions(-) diff --git a/bases/bot_detector/website/api/account_search.py b/bases/bot_detector/website/api/account_search.py index 74b24ece..a599c6a9 100644 --- a/bases/bot_detector/website/api/account_search.py +++ b/bases/bot_detector/website/api/account_search.py @@ -1,20 +1,22 @@ -from bot_detector.website.core.config import BD_API, templates -from fastapi import APIRouter, Form, Request -from fastapi.responses import HTMLResponse - -router = APIRouter() - - -@router.get("/account-search") -async def get_account_search(request: Request) -> HTMLResponse: - return templates.TemplateResponse("pages/account_search.html", {"request": request}) - - -@router.post("/account-search") -async def post_account_search( - request: Request, username: str = Form(...) -) -> HTMLResponse: - # You can access the submitted username using the "username" variable - prediction = await BD_API.get_prediction(name=username) - response = {"request": request, "prediction": prediction} - return templates.TemplateResponse("pages/account_search.html", response) +from urllib.parse import urlencode + +from bot_detector.website.core.config import BD_API, templates +from fastapi import APIRouter, Request +from fastapi.responses import RedirectResponse, Response + +router = APIRouter() + + +@router.get("/account-search") +async def get_account_search(request: Request, name: str = "") -> Response: + stripped = name.strip() + if name != stripped: + url = "/account-search" + if stripped: + url = f"{url}?{urlencode({'name': stripped})}" + return RedirectResponse(url=url, status_code=303) + predictions: list = [] + if stripped: + predictions = await BD_API.get_prediction(name=stripped) + response = {"request": request, "predictions": predictions, "name": stripped} + return templates.TemplateResponse("pages/account_search.html", response) diff --git a/bases/bot_detector/website/app/controllers/bot_detector.py b/bases/bot_detector/website/app/controllers/bot_detector.py index db236bb6..1a5385a8 100644 --- a/bases/bot_detector/website/app/controllers/bot_detector.py +++ b/bases/bot_detector/website/app/controllers/bot_detector.py @@ -1,40 +1,26 @@ -import aiohttp - - -class BotDetector: - def __init__( - self, token: str, base_url: str = "https://api.prd.osrsbotdetector.com" - ): - self.base_url = base_url - self.session = aiohttp.ClientSession() - self.token = token - - async def get_prediction(self, name: str): - url = f"{self.base_url}/v1/prediction?name={name}" - async with self.session.get(url) as response: - data = await response.json() - return data - - async def get_xp_change(self, player_id: str): - url = f"{self.base_url}/v1/hiscore/XPChange?token={self.token}&player_id={player_id}" - async with self.session.get(url) as response: - data = await response.json() - return data - - async def get_highscore_latest(self, player_id: str): - url = f"{self.base_url}/v1/hiscore/Latest?token={self.token}&player_id={player_id}" - async with self.session.get(url) as response: - data = await response.json() - return data - - async def get_player(self, player_name: str): - url = f"{self.base_url}/v1/player?token={self.token}&player_name={player_name}" - async with self.session.get(url) as response: - data = await response.json() - return data - - async def get_project_stats(self): - url = f"{self.base_url}/site/dashboard/projectstats" - async with self.session.get(url) as response: - data = await response.json() - return data +import aiohttp + + +class BotDetector: + def __init__( + self, token: str, base_url: str = "https://api.prd.osrsbotdetector.com" + ): + self.base_url = base_url + self.session = aiohttp.ClientSession() + self.token = token + + async def get_prediction(self, name: str) -> list: + url = f"{self.base_url}/v2/player/prediction" + params = {"name": name, "breakdown": "true"} + async with self.session.get(url, params=params) as response: + if response.status == 404: + return [] + response.raise_for_status() + data: list = await response.json() + return data + + async def get_project_stats(self): + url = f"{self.base_url}/site/dashboard/projectstats" + async with self.session.get(url) as response: + data = await response.json() + return data diff --git a/bases/bot_detector/website/templates/base.html b/bases/bot_detector/website/templates/base.html index 056442b9..352177bd 100644 --- a/bases/bot_detector/website/templates/base.html +++ b/bases/bot_detector/website/templates/base.html @@ -54,9 +54,9 @@ rel="stylesheet"> - - - + + + diff --git a/bases/bot_detector/website/templates/head.html b/bases/bot_detector/website/templates/head.html index 0043b9b7..bae9ab06 100644 --- a/bases/bot_detector/website/templates/head.html +++ b/bases/bot_detector/website/templates/head.html @@ -1,8 +1,8 @@ {% block head %} - + - - + + {% endblock %} \ No newline at end of file diff --git a/bases/bot_detector/website/templates/pages/account_search.html b/bases/bot_detector/website/templates/pages/account_search.html index 4cd3f271..c737af14 100644 --- a/bases/bot_detector/website/templates/pages/account_search.html +++ b/bases/bot_detector/website/templates/pages/account_search.html @@ -1,104 +1,175 @@ -{% extends "base.html" %} - -{% block content %} -
-
-
- -
-
- -
-
- -
-
-
- Bot Detector -
-

Account Search

-

- If you would like to learn more about an Old School RuneScape account, type in the name - below. We cannot provide information within 24 hours of a sighting of a player, - therefore we cannot provide the latest locations and the latest gear information for an - account. -

-
- - -
-
-
- - -
-
-
- -
-
-
-
- - - {% if prediction %} -

Prediction

-
    - {% for key, value in prediction.items() %} - {% if key == "predictions_breakdown" %} -
  • {{ key }}: -
      - {% for sub_key, sub_value in value.items() %} - {% if sub_value != 0 %} -
    • {{ sub_key }}: {{ sub_value * 100 }}%
    • - {% endif %} - {% endfor %} -
    -
  • - {% elif key == "prediction_confidence" %} -
  • {{ key }}: {{ value * 100 }}%
  • - {% else %} -
  • {{ key }}: {{ value }}
  • - {% endif %} - {% endfor %} -
- {% endif %} -
-
-
-
- - -
- -
-
-

Get notified about bans

-
-

- Discord Server -

-
-
-
-
- - -
-
-

Get Bot Detector

-
-

Install plugin in RuneLite:

-
- Plugin installation instructions -
-
-
-
-
-
-
-{% endblock %} +{% extends "base.html" %} + +{% block content %} +
+
+
+ +
+
+ +
+
+ +
+
+
+ Bot Detector +
+

Account Search

+

+ If you would like to learn more about an Old School RuneScape account, type in the name + below. We cannot provide information within 24 hours of a sighting of a player, + therefore we cannot provide the latest locations and the latest gear information for an + account. +

+
+ + +
+
+
+ + +
+
+
+ +
+
+
+
+ + + {% if name and not predictions %} + + {% endif %} + {% if predictions %} + {% for prediction in predictions|sort(attribute='prediction_confidence', reverse=true) %} + {# compute hues: map score 0.0->220 (cool blue) to 1.0->140 (soft green) #} + {% set conf_hue = (220 - (prediction.prediction_confidence * 80))|int %} +
+ +
+
+
+
+
{{ prediction.player_name }}
+
Result snapshot • {{ prediction.created if prediction.created is defined else '' }}
+
+
+ +
+ {# confidence pill uses the same hue but softer #} +
+ {{ (prediction.prediction_confidence * 100)|round(1) }}% +
+
+
+ + +
+
+
Prediction Breakdown
+
Model confidence: {{ (prediction.prediction_confidence * 100)|round(1) }}%
+
+ +
+ {# badges: hue derived per-score (220->140), softer saturation/lightness for subtle look #} + {% for bot_type, score in prediction.predictions_breakdown|dictsort(false, 'value', true) %} + {% if score != 0 %} + {% set hue = (220 - (score * 80))|int %} +
+
+ +
+ {{ bot_type.replace('_', ' ') }} + {{ (score * 100)|round(2) }}% +
+
+ {% endif %} + {% endfor %} +
+ + {# Optional: breakdown as small progress bars for more visual weight #} +
+ {% for bot_type, score in prediction.predictions_breakdown|dictsort(false, 'value', true) %} + {% if score != 0 %} + {% set hue = (220 - (score * 80))|int %} +
+
{{ bot_type.replace('_', ' ') }}
+
+
+
+
+
+
+ {{ (score * 100)|round(2) }}% +
+
+ {% endif %} + {% endfor %} +
+
+
+ {% endfor %} + {% endif %} +
+
+
+
+ + +
+ +
+
+

Get notified about bans

+
+

+ Discord Server +

+
+
+
+
+ + +
+
+

Get Bot Detector

+
+

Install plugin in RuneLite:

+
+ Plugin installation instructions +
+
+
+
+
+
+
+{% endblock %} diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index 473c2c2f..1ddd87c8 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -366,6 +366,7 @@ services: context: . dockerfile: ./projects/website/Dockerfile target: dev + command: ["uvicorn", "bot_detector.website.core.server:app", "--host", "0.0.0.0", "--port", "5000", "--reload", "--reload-dir", "/project/bases", "--reload-dir", "/project/components"] env_file: - .env environment: @@ -374,11 +375,12 @@ services: RELEASE_VERSION: "0.1" PATREON_CLIENT_ID: "" PATREON_CLIENT_SECRET: "" + PYTHONPATH: "/project/bases:/project/components" volumes: - ./bases:/project/bases - ./components:/project/components ports: - - 5000 + - 5000:5000 networks: - botdetector-network healthcheck: