file and folder organization, removal of monoliths
This commit is contained in:
BIN
__pycache__/config.cpython-313.pyc
Normal file
BIN
__pycache__/config.cpython-313.pyc
Normal file
Binary file not shown.
BIN
__pycache__/main.cpython-313.pyc
Normal file
BIN
__pycache__/main.cpython-313.pyc
Normal file
Binary file not shown.
BIN
cogs/__pycache__/assignments.cpython-313.pyc
Normal file
BIN
cogs/__pycache__/assignments.cpython-313.pyc
Normal file
Binary file not shown.
BIN
cogs/__pycache__/commands.cpython-313.pyc
Normal file
BIN
cogs/__pycache__/commands.cpython-313.pyc
Normal file
Binary file not shown.
64
cogs/assignments.py
Normal file
64
cogs/assignments.py
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import asyncio
|
||||||
|
from discord.ext import commands
|
||||||
|
|
||||||
|
class Assignments(commands.Cog):
|
||||||
|
def __init__(self, bot, enemy_queue, active_assignments, enrolled_attackers, hit_check, reassign_delay):
|
||||||
|
self.bot = bot
|
||||||
|
self.enemy_queue = enemy_queue
|
||||||
|
self.active_assignments = active_assignments
|
||||||
|
self.enrolled_attackers = enrolled_attackers
|
||||||
|
self.HIT_CHECK_INTERVAL = hit_check
|
||||||
|
self.REASSIGN_DELAY = reassign_delay
|
||||||
|
|
||||||
|
# Start background task
|
||||||
|
bot.loop.create_task(self.monitor_assignments_loop())
|
||||||
|
|
||||||
|
def get_next_attacker(self):
|
||||||
|
if not self.enrolled_attackers:
|
||||||
|
return None
|
||||||
|
attacker = self.enrolled_attackers[0]
|
||||||
|
self.enrolled_attackers.append(self.enrolled_attackers.pop(0)) # round-robin
|
||||||
|
return attacker
|
||||||
|
|
||||||
|
async def monitor_assignments_loop(self):
|
||||||
|
await self.bot.wait_until_ready()
|
||||||
|
while not self.bot.is_closed():
|
||||||
|
now = asyncio.get_event_loop().time()
|
||||||
|
reassign_list = []
|
||||||
|
for enemy_id, data in list(self.active_assignments.items()):
|
||||||
|
elapsed = now - data["time_assigned"]
|
||||||
|
if elapsed >= self.HIT_CHECK_INTERVAL and elapsed < self.HIT_CHECK_INTERVAL + 5:
|
||||||
|
attacker_user = self.bot.get_user(data["attacker"])
|
||||||
|
if attacker_user:
|
||||||
|
try:
|
||||||
|
await attacker_user.send(
|
||||||
|
f"Reminder: You were assigned **{data['enemy']['name']}** and they are not down yet!"
|
||||||
|
)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
if elapsed >= self.REASSIGN_DELAY:
|
||||||
|
reassign_list.append(enemy_id)
|
||||||
|
|
||||||
|
for enemy_id in reassign_list:
|
||||||
|
info = self.active_assignments.pop(enemy_id)
|
||||||
|
await self.reassign_target(info["enemy"])
|
||||||
|
|
||||||
|
await asyncio.sleep(5)
|
||||||
|
|
||||||
|
async def reassign_target(self, enemy):
|
||||||
|
attacker = self.get_next_attacker()
|
||||||
|
if attacker is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.active_assignments[enemy["id"]] = {
|
||||||
|
"enemy": enemy,
|
||||||
|
"attacker": attacker,
|
||||||
|
"time_assigned": asyncio.get_event_loop().time()
|
||||||
|
}
|
||||||
|
|
||||||
|
attacker_user = self.bot.get_user(attacker)
|
||||||
|
if attacker_user:
|
||||||
|
try:
|
||||||
|
await attacker_user.send(f"Target reassigned to you: **{enemy['name']}**!")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
54
cogs/commands.py
Normal file
54
cogs/commands.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
from discord.ext import commands
|
||||||
|
from services.torn_api import fetch_enemy_members
|
||||||
|
from services.ffscouter import fetch_batch_stats
|
||||||
|
|
||||||
|
class HitCommands(commands.Cog):
|
||||||
|
def __init__(self, bot, enrolled_attackers, enemy_queue):
|
||||||
|
self.bot = bot
|
||||||
|
self.enrolled_attackers = enrolled_attackers
|
||||||
|
self.enemy_queue = enemy_queue
|
||||||
|
|
||||||
|
@commands.command()
|
||||||
|
async def enroll(self, ctx):
|
||||||
|
user_id = ctx.author.id
|
||||||
|
if user_id in self.enrolled_attackers:
|
||||||
|
await ctx.send("You are already enrolled.")
|
||||||
|
return
|
||||||
|
self.enrolled_attackers.append(user_id)
|
||||||
|
await ctx.send(f"{ctx.author.mention} has been enrolled in the hit rotation!")
|
||||||
|
|
||||||
|
@commands.command()
|
||||||
|
async def drop(self, ctx):
|
||||||
|
user_id = ctx.author.id
|
||||||
|
if user_id not in self.enrolled_attackers:
|
||||||
|
await ctx.send("You are not enrolled.")
|
||||||
|
return
|
||||||
|
self.enrolled_attackers.remove(user_id)
|
||||||
|
await ctx.send(f"{ctx.author.mention} has been removed from the rotation.")
|
||||||
|
|
||||||
|
@commands.command()
|
||||||
|
async def stats(self, ctx):
|
||||||
|
members = await fetch_enemy_members()
|
||||||
|
if not members:
|
||||||
|
await ctx.send("No active members found.")
|
||||||
|
return
|
||||||
|
|
||||||
|
ids = [m["id"] for m in members if m.get("status", {}).get("state") in ("Okay", "Idle")]
|
||||||
|
ff_map = await fetch_batch_stats(ids)
|
||||||
|
|
||||||
|
lines = []
|
||||||
|
for m in members:
|
||||||
|
pid = str(m["id"])
|
||||||
|
est = ff_map.get(pid, {}).get("bs_estimate_human", "?")
|
||||||
|
if m.get("status", {}).get("state") not in ("Okay", "Idle"):
|
||||||
|
continue
|
||||||
|
lines.append(f"**{m['name']}** (ID:{pid}) | Lv {m['level']} | Estimated BS: {est}")
|
||||||
|
|
||||||
|
chunk = ""
|
||||||
|
for line in lines:
|
||||||
|
if len(chunk) + len(line) > 1900:
|
||||||
|
await ctx.send(chunk)
|
||||||
|
chunk = ""
|
||||||
|
chunk += line + "\n"
|
||||||
|
if chunk:
|
||||||
|
await ctx.send(chunk)
|
||||||
13
config.py
Normal file
13
config.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Torn API
|
||||||
|
TORN_API_KEY = "9VLK0Wte1BwXOheB"
|
||||||
|
ENEMY_FACTION_ID = 52935
|
||||||
|
YOUR_FACTION_ID = 654321
|
||||||
|
ALLOWED_CHANNEL_ID = 1442876328536707316
|
||||||
|
|
||||||
|
# FFScouter API
|
||||||
|
FFSCOUTER_KEY = "XYmWPO9ZYkLqnv3v"
|
||||||
|
|
||||||
|
# Intervals
|
||||||
|
POLL_INTERVAL = 30
|
||||||
|
HIT_CHECK_INTERVAL = 60
|
||||||
|
REASSIGN_DELAY = 120
|
||||||
324
main.py
324
main.py
@@ -1,324 +1,46 @@
|
|||||||
import discord
|
import discord
|
||||||
import asyncio
|
from discord.ext import commands
|
||||||
import aiohttp
|
from config import ALLOWED_CHANNEL_ID, HIT_CHECK_INTERVAL, REASSIGN_DELAY
|
||||||
from discord.ext import commands
|
from cogs.assignments import Assignments
|
||||||
import re
|
from cogs.commands import HitCommands
|
||||||
|
|
||||||
# ==============================
|
|
||||||
# CONFIGURATION
|
|
||||||
# ==============================
|
|
||||||
|
|
||||||
TORN_API_KEY = "9VLK0Wte1BwXOheB"
|
|
||||||
ENEMY_FACTION_ID = 52935
|
|
||||||
YOUR_FACTION_ID = 654321
|
|
||||||
ALLOWED_CHANNEL_ID = 1442876328536707316
|
|
||||||
FFSCOUTER_KEY = "XYmWPO9ZYkLqnv3v"
|
|
||||||
|
|
||||||
POLL_INTERVAL = 30
|
|
||||||
HIT_CHECK_INTERVAL = 60
|
|
||||||
REASSIGN_DELAY = 120
|
|
||||||
|
|
||||||
intents = discord.Intents.default()
|
intents = discord.Intents.default()
|
||||||
intents.message_content = True
|
intents.message_content = True
|
||||||
|
|
||||||
# ==============================
|
# Global state
|
||||||
# STATE STORAGE
|
|
||||||
# ==============================
|
|
||||||
|
|
||||||
enrolled_attackers = []
|
enrolled_attackers = []
|
||||||
enemy_queue = []
|
enemy_queue = []
|
||||||
active_assignments = {}
|
active_assignments = {}
|
||||||
round_robin_index = 0
|
round_robin_index = 0
|
||||||
|
|
||||||
# ==============================
|
|
||||||
# BOT SUBCLASS
|
|
||||||
# ==============================
|
|
||||||
|
|
||||||
class HitDispatchBot(commands.Bot):
|
class HitDispatchBot(commands.Bot):
|
||||||
async def setup_hook(self):
|
async def setup_hook(self):
|
||||||
# Start background loops
|
# Load cogs with injected state
|
||||||
self.bg_tasks = []
|
await self.add_cog(
|
||||||
self.bg_tasks.append(asyncio.create_task(update_enemy_queue_loop()))
|
Assignments(
|
||||||
self.bg_tasks.append(asyncio.create_task(monitor_assignments_loop()))
|
self,
|
||||||
|
enemy_queue=enemy_queue,
|
||||||
|
active_assignments=active_assignments,
|
||||||
|
enrolled_attackers=enrolled_attackers,
|
||||||
|
hit_check=HIT_CHECK_INTERVAL,
|
||||||
|
reassign_delay=REASSIGN_DELAY
|
||||||
|
)
|
||||||
|
)
|
||||||
|
await self.add_cog(
|
||||||
|
HitCommands(
|
||||||
|
self,
|
||||||
|
enrolled_attackers=enrolled_attackers,
|
||||||
|
enemy_queue=enemy_queue
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
async def cog_check(self, ctx):
|
async def cog_check(self, ctx):
|
||||||
return ctx.channel.id == ALLOWED_CHANNEL_ID
|
return ctx.channel.id == ALLOWED_CHANNEL_ID
|
||||||
|
|
||||||
|
|
||||||
# Create bot now (ONE bot only)
|
|
||||||
bot = HitDispatchBot(command_prefix="!", intents=intents)
|
bot = HitDispatchBot(command_prefix="!", intents=intents)
|
||||||
|
|
||||||
async def fetch_ffscouter_stats(session: aiohttp.ClientSession, torn_id: int):
|
|
||||||
"""
|
|
||||||
Calls FFScouter and returns predicted battle stats.
|
|
||||||
Uses an existing aiohttp session (caller must provide).
|
|
||||||
"""
|
|
||||||
url = f"https://ffscouter.com/api/v1/get-stats?key={FFSCOUTER_KEY}&targets={torn_id}"
|
|
||||||
|
|
||||||
#print(url)
|
|
||||||
|
|
||||||
async with session.get(url) as resp:
|
|
||||||
if resp.status != 200:
|
|
||||||
print(f"FFScouter Error for {torn_id}:", resp.status)
|
|
||||||
return None
|
|
||||||
|
|
||||||
data = await resp.json()
|
|
||||||
# FFScouter v1 returns: {"code":200,"message":"OK","data": {"<id>": {...}}}
|
|
||||||
if "data" not in data:
|
|
||||||
return None
|
|
||||||
inner = data["data"]
|
|
||||||
return inner.get(str(torn_id))
|
|
||||||
|
|
||||||
|
|
||||||
async def fetch_enemy_faction():
|
|
||||||
"""
|
|
||||||
Pulls faction members from Torn (selections=members),
|
|
||||||
then fetches FFScouter stats in a single batch request.
|
|
||||||
Returns a list of enemies with name, id, level, and estimated BS.
|
|
||||||
"""
|
|
||||||
url = (
|
|
||||||
f"https://api.torn.com/v2/faction/{ENEMY_FACTION_ID}"
|
|
||||||
f"?selections=members&key={TORN_API_KEY}"
|
|
||||||
)
|
|
||||||
|
|
||||||
enemies = []
|
|
||||||
|
|
||||||
async with aiohttp.ClientSession() as session:
|
|
||||||
# --- Fetch faction members ---
|
|
||||||
async with session.get(url) as resp:
|
|
||||||
if resp.status != 200:
|
|
||||||
print("Torn faction fetch error:", resp.status)
|
|
||||||
return enemies
|
|
||||||
data = await resp.json()
|
|
||||||
|
|
||||||
members_list = data.get("members", [])
|
|
||||||
if not members_list:
|
|
||||||
return enemies
|
|
||||||
|
|
||||||
# --- Build comma-separated list of IDs ---
|
|
||||||
member_ids = [
|
|
||||||
str(info.get("player_id", info.get("id", 0)))
|
|
||||||
for info in members_list
|
|
||||||
if info.get("status", {}).get("state", "Unknown") in ("Okay", "Idle")
|
|
||||||
]
|
|
||||||
|
|
||||||
if not member_ids:
|
|
||||||
return enemies
|
|
||||||
|
|
||||||
ids_str = ",".join(member_ids)
|
|
||||||
|
|
||||||
# --- Single FFScouter batch request ---
|
|
||||||
ff_url = f"https://ffscouter.com/api/v1/get-stats?key={FFSCOUTER_KEY}&targets={ids_str}"
|
|
||||||
async with session.get(ff_url) as resp:
|
|
||||||
if resp.status != 200:
|
|
||||||
print("FFScouter batch error:", resp.status)
|
|
||||||
ff_data_list = []
|
|
||||||
else:
|
|
||||||
ff_data_list = await resp.json()
|
|
||||||
|
|
||||||
# --- Map FFScouter data by player_id for quick lookup ---
|
|
||||||
ff_map = {str(d["player_id"]): d for d in ff_data_list}
|
|
||||||
|
|
||||||
# --- Build final enemies list ---
|
|
||||||
for info in members_list:
|
|
||||||
pid = str(info.get("player_id", info.get("id", 0)))
|
|
||||||
state = info.get("status", {}).get("state", "Unknown")
|
|
||||||
if state not in ("Okay", "Idle"):
|
|
||||||
continue
|
|
||||||
|
|
||||||
name = info.get("name", "Unknown")
|
|
||||||
level = int(info.get("level", 0))
|
|
||||||
est = ff_map.get(pid, {}).get("bs_estimate_human", "?")
|
|
||||||
|
|
||||||
enemies.append({
|
|
||||||
"id": int(pid),
|
|
||||||
"name": name,
|
|
||||||
"level": level,
|
|
||||||
"estimate": est
|
|
||||||
})
|
|
||||||
|
|
||||||
return enemies
|
|
||||||
|
|
||||||
async def update_enemy_queue_loop():
|
|
||||||
global enemy_queue
|
|
||||||
|
|
||||||
await bot.wait_until_ready()
|
|
||||||
|
|
||||||
while not bot.is_closed():
|
|
||||||
print("Refreshing enemy list...")
|
|
||||||
enemy_queue = await fetch_enemy_faction()
|
|
||||||
print(f"Enemy queue updated: {len(enemy_queue)} valid targets.")
|
|
||||||
await asyncio.sleep(POLL_INTERVAL)
|
|
||||||
|
|
||||||
|
|
||||||
# ==============================
|
|
||||||
# ASSIGNMENT SYSTEM
|
|
||||||
# ==============================
|
|
||||||
|
|
||||||
def get_next_attacker():
|
|
||||||
global round_robin_index
|
|
||||||
|
|
||||||
if not enrolled_attackers:
|
|
||||||
return None
|
|
||||||
|
|
||||||
attacker = enrolled_attackers[round_robin_index]
|
|
||||||
round_robin_index = (round_robin_index + 1) % len(enrolled_attackers)
|
|
||||||
return attacker
|
|
||||||
|
|
||||||
|
|
||||||
async def assign_next_target():
|
|
||||||
if not enemy_queue:
|
|
||||||
return None, None
|
|
||||||
|
|
||||||
# Pop the weakest enemy (already sorted by score_value)
|
|
||||||
enemy = enemy_queue.pop(0)
|
|
||||||
attacker = get_next_attacker()
|
|
||||||
|
|
||||||
if attacker is None:
|
|
||||||
return None, None
|
|
||||||
|
|
||||||
active_assignments[enemy["id"]] = {
|
|
||||||
"enemy": enemy,
|
|
||||||
"attacker": attacker,
|
|
||||||
"time_assigned": asyncio.get_event_loop().time()
|
|
||||||
}
|
|
||||||
|
|
||||||
return enemy, attacker
|
|
||||||
|
|
||||||
async def monitor_assignments_loop():
|
|
||||||
await bot.wait_until_ready()
|
|
||||||
|
|
||||||
while not bot.is_closed():
|
|
||||||
now = asyncio.get_event_loop().time()
|
|
||||||
reassign_list = []
|
|
||||||
|
|
||||||
for enemy_id, data in list(active_assignments.items()):
|
|
||||||
elapsed = now - data["time_assigned"]
|
|
||||||
|
|
||||||
if elapsed >= HIT_CHECK_INTERVAL and elapsed < HIT_CHECK_INTERVAL + 5:
|
|
||||||
attacker_user = bot.get_user(data["attacker"])
|
|
||||||
if attacker_user:
|
|
||||||
try:
|
|
||||||
await attacker_user.send(
|
|
||||||
f"Reminder: You were assigned **{data['enemy']['name']}** and they are not down yet!"
|
|
||||||
)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if elapsed >= REASSIGN_DELAY:
|
|
||||||
reassign_list.append(enemy_id)
|
|
||||||
|
|
||||||
for enemy_id in reassign_list:
|
|
||||||
info = active_assignments.pop(enemy_id)
|
|
||||||
await reassign_target(info["enemy"])
|
|
||||||
|
|
||||||
await asyncio.sleep(5)
|
|
||||||
|
|
||||||
|
|
||||||
async def reassign_target(enemy):
|
|
||||||
attacker = get_next_attacker()
|
|
||||||
|
|
||||||
if attacker is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
active_assignments[enemy["id"]] = {
|
|
||||||
"enemy": enemy,
|
|
||||||
"attacker": attacker,
|
|
||||||
"time_assigned": asyncio.get_event_loop().time()
|
|
||||||
}
|
|
||||||
|
|
||||||
attacker_user = bot.get_user(attacker)
|
|
||||||
if attacker_user:
|
|
||||||
try:
|
|
||||||
await attacker_user.send(
|
|
||||||
f"Target reassigned to you: **{enemy['name']}**!"
|
|
||||||
)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
# ==============================
|
|
||||||
# COMMANDS
|
|
||||||
# ==============================
|
|
||||||
|
|
||||||
@bot.command()
|
|
||||||
async def enroll(ctx):
|
|
||||||
user_id = ctx.author.id
|
|
||||||
|
|
||||||
if user_id in enrolled_attackers:
|
|
||||||
await ctx.send("You are already enrolled.")
|
|
||||||
return
|
|
||||||
|
|
||||||
enrolled_attackers.append(user_id)
|
|
||||||
await ctx.send(f"{ctx.author.mention} has been enrolled in the hit rotation!")
|
|
||||||
|
|
||||||
|
|
||||||
@bot.command()
|
|
||||||
async def drop(ctx):
|
|
||||||
user_id = ctx.author.id
|
|
||||||
|
|
||||||
if user_id not in enrolled_attackers:
|
|
||||||
await ctx.send("You are not enrolled.")
|
|
||||||
return
|
|
||||||
|
|
||||||
enrolled_attackers.remove(user_id)
|
|
||||||
await ctx.send(f"{ctx.author.mention} has been removed from the rotation.")
|
|
||||||
|
|
||||||
|
|
||||||
@bot.command()
|
|
||||||
async def next(ctx):
|
|
||||||
enemy, attacker = await assign_next_target()
|
|
||||||
|
|
||||||
if enemy is None:
|
|
||||||
await ctx.send("No targets available or no attackers enrolled.")
|
|
||||||
return
|
|
||||||
|
|
||||||
attacker_user = bot.get_user(attacker)
|
|
||||||
await ctx.send(f"Assigned **{enemy['name']}** → <@{attacker}>")
|
|
||||||
|
|
||||||
if attacker_user:
|
|
||||||
try:
|
|
||||||
await attacker_user.send(
|
|
||||||
f"Hit assignment: **{enemy['name']}**"
|
|
||||||
)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
@bot.command()
|
|
||||||
async def stats(ctx):
|
|
||||||
enemies = await fetch_enemy_faction()
|
|
||||||
|
|
||||||
if not enemies:
|
|
||||||
await ctx.send("No active members found.")
|
|
||||||
return
|
|
||||||
|
|
||||||
lines = [
|
|
||||||
f"**{e['name']}** (ID:{e['id']}) | Lv {e['level']} | Estimated BS: {e['estimate']}"
|
|
||||||
for e in enemies
|
|
||||||
]
|
|
||||||
|
|
||||||
# Discord chunking
|
|
||||||
chunk = ""
|
|
||||||
for line in lines:
|
|
||||||
if len(chunk) + len(line) > 1900:
|
|
||||||
await ctx.send(chunk)
|
|
||||||
chunk = ""
|
|
||||||
chunk += line + "\n"
|
|
||||||
if chunk:
|
|
||||||
await ctx.send(chunk)
|
|
||||||
|
|
||||||
|
|
||||||
# ==============================
|
|
||||||
# BOT READY
|
|
||||||
# ==============================
|
|
||||||
|
|
||||||
@bot.event
|
@bot.event
|
||||||
async def on_ready():
|
async def on_ready():
|
||||||
print(f"Logged in as {bot.user.name}")
|
print(f"Logged in as {bot.user.name}")
|
||||||
|
|
||||||
|
|
||||||
# ==============================
|
|
||||||
# RUN BOT
|
|
||||||
# ==============================
|
|
||||||
|
|
||||||
bot.run("MTQ0Mjg3NjU3NTUzMDg3NzAxMQ.GNuHPr.UreuYD1B7YYjfsbfRcEbhFyjyqvhQDepRCN4kk")
|
bot.run("MTQ0Mjg3NjU3NTUzMDg3NzAxMQ.GNuHPr.UreuYD1B7YYjfsbfRcEbhFyjyqvhQDepRCN4kk")
|
||||||
|
|||||||
BIN
services/__pycache__/ffscouter.cpython-313.pyc
Normal file
BIN
services/__pycache__/ffscouter.cpython-313.pyc
Normal file
Binary file not shown.
BIN
services/__pycache__/torn_api.cpython-313.pyc
Normal file
BIN
services/__pycache__/torn_api.cpython-313.pyc
Normal file
Binary file not shown.
22
services/ffscouter.py
Normal file
22
services/ffscouter.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import aiohttp
|
||||||
|
from config import FFSCOUTER_KEY
|
||||||
|
|
||||||
|
async def fetch_batch_stats(ids: list[int]):
|
||||||
|
"""
|
||||||
|
Fetches predicted stats for a list of Torn IDs in a single FFScouter request.
|
||||||
|
Returns dict keyed by player_id.
|
||||||
|
"""
|
||||||
|
if not ids:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
ids_str = ",".join(map(str, ids))
|
||||||
|
url = f"https://ffscouter.com/api/v1/get-stats?key={FFSCOUTER_KEY}&targets={ids_str}"
|
||||||
|
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
async with session.get(url) as resp:
|
||||||
|
if resp.status != 200:
|
||||||
|
print("FFScouter batch error:", resp.status)
|
||||||
|
return {}
|
||||||
|
data = await resp.json()
|
||||||
|
|
||||||
|
return {str(d["player_id"]): d for d in data}
|
||||||
12
services/torn_api.py
Normal file
12
services/torn_api.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import aiohttp
|
||||||
|
from config import TORN_API_KEY, ENEMY_FACTION_ID
|
||||||
|
|
||||||
|
async def fetch_enemy_members():
|
||||||
|
url = f"https://api.torn.com/v2/faction/{ENEMY_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("Torn faction fetch error:", resp.status)
|
||||||
|
return []
|
||||||
|
data = await resp.json()
|
||||||
|
return data.get("members", [])
|
||||||
Reference in New Issue
Block a user