Stylized member cards with static fields
This commit is contained in:
Binary file not shown.
@@ -3,31 +3,34 @@ import aiohttp
|
||||
import json
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
from config import TORN_API_KEY, ENEMY_FACTION_ID, YOUR_FACTION_ID
|
||||
from config import TORN_API_KEY
|
||||
from .ffscouter import fetch_batch_stats
|
||||
|
||||
ENEMY_FILE = Path("data/enemy_faction.json")
|
||||
FRIENDLY_FILE = Path("data/friendly_faction.json")
|
||||
FRIENDLY_MEMBERS_FILE = Path("data/friendly_members.json")
|
||||
FRIENDLY_STATUS_FILE = Path("data/friendly_status.json")
|
||||
ENEMY_MEMBERS_FILE = Path("data/enemy_members.json")
|
||||
ENEMY_STATUS_FILE = Path("data/enemy_status.json")
|
||||
|
||||
# Track running tasks + current faction IDs
|
||||
enemy_task = None
|
||||
friendly_task = None
|
||||
# Tasks
|
||||
friendly_status_task = None
|
||||
enemy_status_task = None
|
||||
|
||||
current_enemy_id = None
|
||||
current_friendly_id = None
|
||||
# Locks
|
||||
friendly_lock = asyncio.Lock()
|
||||
enemy_lock = asyncio.Lock()
|
||||
|
||||
|
||||
async def fetch_and_save_faction(faction_id: int, file_path: Path) -> bool:
|
||||
"""
|
||||
Fetches faction members from Torn, fetches their estimated BS from FFScouter,
|
||||
and saves everything to a JSON file.
|
||||
"""
|
||||
# -----------------------------
|
||||
# Static population (once)
|
||||
# -----------------------------
|
||||
async def populate_faction(faction_id: int, members_file: Path, status_file: Path):
|
||||
"""Fetch members + FFScouter estimates once and save static info + initial status."""
|
||||
url = f"https://api.torn.com/v2/faction/{faction_id}?selections=members&key={TORN_API_KEY}"
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(url) as resp:
|
||||
if resp.status != 200:
|
||||
print(f"Torn faction fetch error: {resp.status}")
|
||||
print(f"Error fetching faction {faction_id}: {resp.status}")
|
||||
return False
|
||||
data = await resp.json()
|
||||
|
||||
@@ -35,92 +38,95 @@ async def fetch_and_save_faction(faction_id: int, file_path: Path) -> bool:
|
||||
if not members_list:
|
||||
return False
|
||||
|
||||
# Build list of IDs (Torn uses 'id', not 'player_id')
|
||||
member_ids = [info.get("id") for info in members_list if "id" in info]
|
||||
member_ids = [m.get("id") for m in members_list if "id" in m]
|
||||
if not member_ids:
|
||||
return False
|
||||
|
||||
# Fetch batch FFScouter stats
|
||||
ff_data = await fetch_batch_stats(member_ids) # returns dict keyed by player_id
|
||||
|
||||
# Build final faction data
|
||||
faction_data = []
|
||||
for info in members_list:
|
||||
pid = info.get("id")
|
||||
if pid is None:
|
||||
continue
|
||||
# Fetch FFScouter data once
|
||||
ff_data = await fetch_batch_stats(member_ids)
|
||||
|
||||
# Build static member list
|
||||
members = []
|
||||
status_data = {}
|
||||
for m in members_list:
|
||||
pid = m["id"]
|
||||
est = ff_data.get(str(pid), {}).get("bs_estimate_human", "?")
|
||||
member = {
|
||||
"id": pid,
|
||||
"name": info.get("name", "Unknown"),
|
||||
"level": info.get("level", 0),
|
||||
"status": info.get("status", {}).get("state", "Unknown"),
|
||||
"estimate": est
|
||||
"name": m.get("name", "Unknown"),
|
||||
"level": m.get("level", 0),
|
||||
"estimate": est,
|
||||
}
|
||||
faction_data.append(member)
|
||||
members.append(member)
|
||||
# initial status
|
||||
status_data[pid] = {"status": m.get("status", {}).get("state", "Unknown")}
|
||||
|
||||
# Save to file
|
||||
file_path.parent.mkdir(exist_ok=True, parents=True) # ensure folder exists
|
||||
with open(file_path, "w", encoding="utf-8") as f:
|
||||
json.dump(faction_data, f, indent=2)
|
||||
# Save members
|
||||
members_file.parent.mkdir(exist_ok=True, parents=True)
|
||||
with open(members_file, "w", encoding="utf-8") as f:
|
||||
json.dump(members, f, indent=2)
|
||||
|
||||
# Save initial status
|
||||
with open(status_file, "w", encoding="utf-8") as f:
|
||||
json.dump(status_data, f, indent=2)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
||||
|
||||
#Loop for the constant update of members and the stop function
|
||||
|
||||
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.
|
||||
"""
|
||||
# -----------------------------
|
||||
# Status refresh loop
|
||||
# -----------------------------
|
||||
async def refresh_status_loop(faction_id: int, status_file: Path, lock: asyncio.Lock, interval: int):
|
||||
"""Refresh only status from Torn API periodically."""
|
||||
while True:
|
||||
try:
|
||||
await fetch_and_save_faction(faction_id, file_path)
|
||||
url = f"https://api.torn.com/v2/faction/{faction_id}?selections=members&key={TORN_API_KEY}"
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(url) as resp:
|
||||
if resp.status != 200:
|
||||
print(f"Status fetch error {resp.status}")
|
||||
await asyncio.sleep(interval)
|
||||
continue
|
||||
data = await resp.json()
|
||||
|
||||
members_list = data.get("members", [])
|
||||
status_data = {m["id"]: {"status": m.get("status", {}).get("state", "Unknown")} for m in members_list}
|
||||
|
||||
# Save status safely
|
||||
async with lock:
|
||||
with open(status_file, "w", encoding="utf-8") as f:
|
||||
json.dump(status_data, f, indent=2)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error during faction loop for {faction_id}: {e}")
|
||||
print(f"Error in status loop for {faction_id}: {e}")
|
||||
|
||||
await asyncio.sleep(interval)
|
||||
|
||||
|
||||
# -----------------------------
|
||||
# Helper functions for endpoints
|
||||
# -----------------------------
|
||||
async def populate_friendly(faction_id: int):
|
||||
return await populate_faction(faction_id, FRIENDLY_MEMBERS_FILE, FRIENDLY_STATUS_FILE)
|
||||
|
||||
#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
|
||||
async def populate_enemy(faction_id: int):
|
||||
return await populate_faction(faction_id, ENEMY_MEMBERS_FILE, ENEMY_STATUS_FILE)
|
||||
|
||||
# Start new loop
|
||||
enemy_task = asyncio.create_task(
|
||||
faction_loop(new_faction_id, ENEMY_FILE, interval)
|
||||
|
||||
async def start_friendly_status_loop(faction_id: int, interval: int):
|
||||
global friendly_status_task
|
||||
if friendly_status_task and not friendly_status_task.done():
|
||||
friendly_status_task.cancel()
|
||||
friendly_status_task = asyncio.create_task(
|
||||
refresh_status_loop(faction_id, FRIENDLY_STATUS_FILE, friendly_lock, 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)
|
||||
async def start_enemy_status_loop(faction_id: int, interval: int):
|
||||
global enemy_status_task
|
||||
if enemy_status_task and not enemy_status_task.done():
|
||||
enemy_status_task.cancel()
|
||||
enemy_status_task = asyncio.create_task(
|
||||
refresh_status_loop(faction_id, ENEMY_STATUS_FILE, enemy_lock, interval)
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user