Compare commits

...

2 Commits

Author SHA1 Message Date
79d0012d66 proper functoin calls for faction data 2025-11-26 17:04:15 -05:00
1dde34d266 skeleton webpage 2025-11-26 16:35:23 -05:00
12 changed files with 256 additions and 8 deletions

View File

@@ -7,3 +7,11 @@ Features:
- match targets up with members that have the appropriate stats - match targets up with members that have the appropriate stats
- dashboard to see who is supposed to hit who - dashboard to see who is supposed to hit who
- maybe also enemy stats - maybe also enemy stats
ToDo:
- move interval button to a neutral spot
- sections to list faction memebers
- needs to be movable objects
- Assignment pools depending on stats
- players will be round-robin queued their targets from here

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

68
main.py
View File

@@ -4,10 +4,59 @@ from config import ALLOWED_CHANNEL_ID, HIT_CHECK_INTERVAL, REASSIGN_DELAY
from cogs.assignments import Assignments from cogs.assignments import Assignments
from cogs.commands import HitCommands from cogs.commands import HitCommands
import asyncio
import uvicorn
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from pydantic import BaseModel
from services.torn_api import update_enemy_faction, update_friendly_faction
# -----------------------------
# FastAPI setup
# -----------------------------
app = FastAPI()
templates = Jinja2Templates(directory="templates")
app.mount("/static", StaticFiles(directory="static"), name="static")
# -----------------------------
# Dashboard page
# -----------------------------
@app.get("/", response_class=HTMLResponse)
async def dashboard(request: Request):
print(">>> DASHBOARD ROUTE LOADED")
return templates.TemplateResponse("dashboard.html", {"request": request})
# -----------------------------
# Pydantic model for JSON payloads
# -----------------------------
class FactionRequest(BaseModel):
faction_id: int
interval: int
# -----------------------------
# API Endpoints
# -----------------------------
@app.post("/api/update_enemy_faction")
async def api_enemy(data: FactionRequest):
await update_enemy_faction(data.faction_id, data.interval)
return {"status": "enemy loop running", "id": data.faction_id, "interval": data.interval}
@app.post("/api/update_friendly_faction")
async def api_friendly(data: FactionRequest):
await update_friendly_faction(data.faction_id, data.interval)
return {"status": "friendly loop running", "id": data.faction_id, "interval": data.interval}
# -----------------------------
# Discord bot setup
# -----------------------------
intents = discord.Intents.default() intents = discord.Intents.default()
intents.message_content = True intents.message_content = True
# Global state
enrolled_attackers = [] enrolled_attackers = []
enemy_queue = [] enemy_queue = []
active_assignments = {} active_assignments = {}
@@ -43,4 +92,19 @@ bot = HitDispatchBot(command_prefix="!", intents=intents)
async def on_ready(): async def on_ready():
print(f"Logged in as {bot.user.name}") print(f"Logged in as {bot.user.name}")
bot.run("MTQ0Mjg3NjU3NTUzMDg3NzAxMQ.GNuHPr.UreuYD1B7YYjfsbfRcEbhFyjyqvhQDepRCN4kk") TOKEN = "YOUR_DISCORD_TOKEN"
async def start_bot():
await bot.start(TOKEN)
# -----------------------------
# Main entry
# -----------------------------
if __name__ == "__main__":
loop = asyncio.get_event_loop()
# Start Discord bot in background
loop.create_task(start_bot())
# Run FastAPI (this will keep the loop alive)
uvicorn.run(app, host="127.0.0.1", port=8000)

Binary file not shown.

Binary file not shown.

View File

@@ -1,6 +1,7 @@
# services/torn_api.py # services/torn_api.py
import aiohttp import aiohttp
import json import json
import asyncio
from pathlib import Path from pathlib import Path
from config import TORN_API_KEY, ENEMY_FACTION_ID, YOUR_FACTION_ID from config import TORN_API_KEY, ENEMY_FACTION_ID, YOUR_FACTION_ID
from .ffscouter import fetch_batch_stats from .ffscouter import fetch_batch_stats
@@ -8,6 +9,13 @@ from .ffscouter import fetch_batch_stats
ENEMY_FILE = Path("data/enemy_faction.json") ENEMY_FILE = Path("data/enemy_faction.json")
FRIENDLY_FILE = Path("data/friendly_faction.json") FRIENDLY_FILE = Path("data/friendly_faction.json")
# Track running tasks + current faction IDs
enemy_task = None
friendly_task = None
current_enemy_id = None
current_friendly_id = None
async def fetch_and_save_faction(faction_id: int, file_path: Path) -> bool: async def fetch_and_save_faction(faction_id: int, file_path: Path) -> bool:
""" """
@@ -59,10 +67,59 @@ async def fetch_and_save_faction(faction_id: int, file_path: Path) -> bool:
return True return True
# Wrappers for clarity
async def update_enemy_faction() -> bool:
return await fetch_and_save_faction(ENEMY_FACTION_ID, ENEMY_FILE)
async def update_friendly_faction() -> bool: #Loop for the constant update of members and the stop function
return await fetch_and_save_faction(YOUR_FACTION_ID, FRIENDLY_FILE)
async def stop_task_if_running(task: asyncio.Task | None):
"""Cancel an existing running task safely."""
if task and not task.done():
task.cancel()
try:
await task
except asyncio.CancelledError:
pass
async def faction_loop(faction_id: int, file_path: Path, interval: int):
"""
Runs fetch_and_save_faction() in a loop forever, waiting `interval`
seconds between iterations.
"""
while True:
try:
await fetch_and_save_faction(faction_id, file_path)
except Exception as e:
print(f"Error during faction loop for {faction_id}: {e}")
await asyncio.sleep(interval)
#Functions to call the loop, maybe add one to just call once?
async def update_enemy_faction(new_faction_id: int, interval: int):
global enemy_task, current_enemy_id
# If faction ID changes → stop old loop
if new_faction_id != current_enemy_id:
print(f"[ENEMY] Changing faction from {current_enemy_id}{new_faction_id}")
await stop_task_if_running(enemy_task)
current_enemy_id = new_faction_id
# Start new loop
enemy_task = asyncio.create_task(
faction_loop(new_faction_id, ENEMY_FILE, interval)
)
async def update_friendly_faction(new_faction_id: int, interval: int):
global friendly_task, current_friendly_id
if new_faction_id != current_friendly_id:
print(f"[FRIENDLY] Changing faction from {current_friendly_id}{new_faction_id}")
await stop_task_if_running(friendly_task)
current_friendly_id = new_faction_id
friendly_task = asyncio.create_task(
faction_loop(new_faction_id, FRIENDLY_FILE, interval)
)

36
static/dashboard.js Normal file
View File

@@ -0,0 +1,36 @@
async function updateEnemy() {
const factionId = parseInt(document.getElementById("enemyId").value);
const interval = parseInt(document.getElementById("refreshInterval").value);
if (!factionId || !interval) {
alert("Please enter Enemy Faction ID and Refresh Interval!");
return;
}
await fetch(`/api/update_enemy_faction`, {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({ faction_id: factionId, interval: interval })
});
}
async function updateFriendly() {
const factionId = parseInt(document.getElementById("friendlyId").value);
if (!factionId) {
alert("Please enter Friendly Faction ID!");
return;
}
const interval = parseInt(document.getElementById("refreshInterval").value);
if (!interval) {
alert("Please enter Refresh Interval!");
return;
}
await fetch(`/api/update_friendly_faction`, {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({ faction_id: factionId, interval: interval })
});
}

55
static/styles.css Normal file
View File

@@ -0,0 +1,55 @@
body {
font-family: Arial, sans-serif;
background-color: #1e1e2f;
color: #f0f0f0;
margin: 0;
padding: 0;
}
.container {
max-width: 800px;
margin: 2rem auto;
padding: 2rem;
background-color: #2c2c3e;
border-radius: 12px;
box-shadow: 0 0 20px rgba(0,0,0,0.5);
}
h1 {
text-align: center;
color: #ffcc00;
}
.faction-section {
background-color: #3a3a4d;
margin: 1.5rem 0;
padding: 1rem 1.5rem;
border-radius: 8px;
}
.faction-section h2 {
color: #66ccff;
}
input[type="number"] {
padding: 0.5rem;
margin-right: 0.5rem;
border-radius: 6px;
border: none;
width: 150px;
}
button {
padding: 0.5rem 1rem;
border-radius: 6px;
border: none;
background-color: #66ccff;
color: #1e1e2f;
font-weight: bold;
cursor: pointer;
transition: background-color 0.2s;
}
button:hover {
background-color: #3399ff;
}

28
templates/dashboard.html Normal file
View File

@@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>War Dashboard</title>
<link rel="stylesheet" href="/static/styles.css">
</head>
<body>
<div class="container">
<h1>War Dashboard</h1>
<div class="faction-section">
<h2>Enemy Faction</h2>
<input type="number" id="enemyId" placeholder="Enemy Faction ID">
<input type="number" id="refreshInterval" placeholder="Refresh Interval (sec)">
<button onclick="updateEnemy()">Refresh Enemy</button>
</div>
<div class="faction-section">
<h2>Friendly Faction</h2>
<input type="number" id="friendlyId" placeholder="Friendly Faction ID">
<button onclick="updateFriendly()">Refresh Friendly</button>
</div>
</div>
<script src="/static/dashboard.js"></script>
</body>
</html>