Compare commits
2 Commits
dbfd3537fe
...
79d0012d66
| Author | SHA1 | Date | |
|---|---|---|---|
| 79d0012d66 | |||
| 1dde34d266 |
@@ -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
|
||||||
BIN
__pycache__/config.cpython-311.pyc
Normal file
BIN
__pycache__/config.cpython-311.pyc
Normal file
Binary file not shown.
BIN
__pycache__/main.cpython-311.pyc
Normal file
BIN
__pycache__/main.cpython-311.pyc
Normal file
Binary file not shown.
BIN
cogs/__pycache__/assignments.cpython-311.pyc
Normal file
BIN
cogs/__pycache__/assignments.cpython-311.pyc
Normal file
Binary file not shown.
BIN
cogs/__pycache__/commands.cpython-311.pyc
Normal file
BIN
cogs/__pycache__/commands.cpython-311.pyc
Normal file
Binary file not shown.
68
main.py
68
main.py
@@ -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)
|
||||||
|
|||||||
BIN
services/__pycache__/ffscouter.cpython-311.pyc
Normal file
BIN
services/__pycache__/ffscouter.cpython-311.pyc
Normal file
Binary file not shown.
BIN
services/__pycache__/torn_api.cpython-311.pyc
Normal file
BIN
services/__pycache__/torn_api.cpython-311.pyc
Normal file
Binary file not shown.
@@ -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
36
static/dashboard.js
Normal 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
55
static/styles.css
Normal 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
28
templates/dashboard.html
Normal 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>
|
||||||
Reference in New Issue
Block a user