Skip to content

Instantly share code, notes, and snippets.

@ispanos
Last active February 8, 2026 22:42
Show Gist options
  • Select an option

  • Save ispanos/f435342e15ba53d9ef02763c6aa75319 to your computer and use it in GitHub Desktop.

Select an option

Save ispanos/f435342e15ba53d9ef02763c6aa75319 to your computer and use it in GitHub Desktop.
sr_checker_lib.py
import argparse
import csv
import re
import subprocess
import tempfile
from pathlib import Path
from typing import Any, Dict
import pandas as pd
import requests
HIGH_VALUE_ITEMS = (
[
"Badge of the Swarmguard",
"Gloves of the Primordial Burrower",
"Dark Edge of Insanity",
"Eyestalk Waist Cord",
"Eye of C'Thun",
"Yshgo'lar, Cowl of Fanatical Devotion",
"Spotted Qiraji Battle Tank",
]
+ [
"Kiss of the Spider",
"Wraith Blade",
"Band of Unnatural Forces",
"Corrupted Ashbringer",
"Eye of the Dead",
"The Restrained Essence of Sapphiron",
"Cloak of the Necropolis",
"Slayer's Crest",
"Gressil, Dawn of Ruin",
"Might of Menethil",
"Plagued Riding Spider",
"The Hungering Cold",
]
+ [
"Mechanical Horse",
"Raka'shishi, Spear of the Adrift Hunt",
"Felforged Dreadhound",
"Heart of Mephistroth",
"Kirel'narak, the Death Sentence",
"Thunderfall, Stormhammer of the Chief Thane",
"Shar'tateth, the Shattered Edge",
"Ephemeral Pendant",
"Shieldrender Talisman",
]
)
raid_codes_map = {
"aq40": 99,
"kara40": 109,
"zg": 100,
"ony": 97,
"naxx": 96,
"es": 102,
}
def norm_item(s: str) -> str:
"""Normalize item strings for matching."""
s = (s or "").strip().lower()
s = re.sub(r"\s+", " ", s)
# remove common punctuation that may differ across exports
s = re.sub(r"[’'`]", "", s)
s = re.sub(r"[\(\)\[\]\{\}]", "", s)
s = re.sub(r"\s*-\s*", "-", s)
return s
def get_exclusive_items(list_of_exclusive_items):
with open(list_of_exclusive_items, "r") as rf:
return [norm_item(line) for line in rf]
def download_raidres_data(
*, event_code: str = None, raid_code: str = None, timeout: int = 30
) -> Dict[str, Any]:
"""
Download https://raidres.top/api/events/<event_code> and
https://raidres.top/raids/raid_<raid_code>.json using browser-like headers.
If you still get 403:
- try adding your own cookies (see `requests.Session()` and `session.cookies`)
- or run it from the same machine/browser session and copy cookies
"""
if event_code is None and raid_code is None:
raise ValueError("Either event_code or raid_code must be provided")
if event_code is not None and raid_code is not None:
raise ValueError("Only one of event_code or raid_code must be provided")
if event_code is not None:
url = f"https://raidres.top/api/events/{event_code}"
else:
url = f"https://raidres.top/raids/raid_{raid_code}.json"
headers = {
"User-Agent": (
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/120.0 Safari/537.36"
),
"Accept": "application/json, text/plain, */*",
"Referer": url,
"Origin": "https://raidres.top",
}
with requests.Session() as s:
r = s.get(url, headers=headers, timeout=timeout)
# one simple retry without Referer/Origin sometimes helps
if r.status_code == 403:
r = s.get(
url,
headers={**headers},
timeout=timeout,
)
r.raise_for_status()
return r.json()
def get_item_name_from_raidres_id(sr_raid_item_id, name_map):
for item in name_map["raidItems"]:
item_id = item["id"]
if item_id == sr_raid_item_id:
return item["name"]
def get_boss_name_from_raidres_id(sr_raid_item_id, name_map):
raidBosses_returned = []
for item in name_map["raidItems"]:
item_id = item["id"]
if item_id == sr_raid_item_id:
raid_boss_ids = item["raidBosses"]
for raid_boss_id in raid_boss_ids:
raidBosses = name_map["raidBosses"]
for boss in raidBosses:
if boss["id"] == raid_boss_id:
raidBosses_returned.append(boss["name"])
return raidBosses_returned
def build_sr_df(payload: Dict[str, Any], name_map) -> pd.DataFrame:
"""
Create a DataFrame with columns:
ID, Item, Boss, Attendee, Comment, SR+
Loops over payload['reservations'] and extracts:
- character.name
- srPlus.value
- comment
- id
- raidItemId
Then resolves Item/Boss via helper functions:
get_item_name_from_raidres_id(raid_item_id)
get_boss_name_from_raidres_id(raid_item_id)
"""
# cols = ["id", "item", "boss", "attendee", "comment", "sr+"]
reservations = payload.get("reservations", [])
if not isinstance(reservations, list):
raise TypeError(
f"payload['reservations'] must be a list, got {type(reservations)}"
)
rows = []
for sr_line in reservations:
# Required fields (will raise KeyError if missing; change to .get(...) if you prefer)
sr_attendee = sr_line["character"]["name"]
sr_attendee_spec = sr_line["character"]["specialization"]
sr_plus = sr_line["srPlus"]["value"]
sr_comment = sr_line.get("comment", "")
sr_id = sr_line["id"]
sr_raid_item_id = sr_line["raidItemId"]
# Resolve item + boss from raidItemId
sr_item_name = get_item_name_from_raidres_id(sr_raid_item_id, name_map)
sr_boss_names = get_boss_name_from_raidres_id(sr_raid_item_id, name_map)
rows.append(
{
"id": sr_id,
"item": sr_item_name,
"boss": sr_boss_names,
"attendee": sr_attendee,
"loot_spec": sr_attendee_spec,
"comment": sr_comment,
"sr+": sr_plus,
# 'sr_raid_item_id': sr_raid_item_id,
}
)
df = pd.DataFrame(rows)
df["item_norm"] = df["item"].map(norm_item)
return df
def get_violation_list(sr_df):
# count SRs per player
sr_counts = (
sr_df.groupby("attendee")
.agg(
total_srs=("item", "count"),
high_value_srs=("is_high_value", "sum"),
)
.reset_index()
)
violations = []
for _, row in sr_counts.iterrows():
reasons = None
if row.high_value_srs >= 1 and row.total_srs > 1:
reasons = "High-value item reserved with other items"
if row.high_value_srs > 1:
reasons = "More than one high-value item reserved"
if reasons:
violations.append(
{
"attendee": row.attendee,
"total_srs": row.total_srs,
"high_value_srs": row.high_value_srs,
"violation": reasons,
}
)
return violations
def get_sr_df(raidres_event_code, raid, high_value_items=None):
raid_code = raid_codes_map.get(raid)
if raid_code is None:
raise ValueError(
f"Invalid raid name: {raid}. Valid options: {list(raid_codes_map.keys())}"
)
if high_value_items is None:
high_value_items = [norm_item(item) for item in HIGH_VALUE_ITEMS]
else:
high_value_items = [norm_item(item) for item in high_value_items]
sr_df = build_sr_df(
download_raidres_data(event_code=raidres_event_code),
download_raidres_data(raid_code=raid_code),
)
sr_df["is_high_value"] = sr_df["item_norm"].isin(high_value_items)
return sr_df
def get_violation_output(sr_df):
violations = get_violation_list(sr_df)
if violations != []:
violations_df = pd.DataFrame(violations)
violators = violations_df["attendee"].to_list()
violations_df_full = sr_df[sr_df["attendee"].isin(violators)]
violations_df_full = violations_df_full
violations_df_full_by_char = (
violations_df_full.assign(
row=lambda d: d.groupby(
[
"attendee",
]
).cumcount()
+ 1
)
.set_index(["attendee", "item", "row"])
.sort_index()
.drop(columns=["item_norm", "id"])
)
return violations_df, violations_df_full_by_char
else:
print("No violations found.")
def get_participants_from_logs(event_code: str, *, timeout: int = 30) -> Dict[str, Any]:
url = f"https://www.turtlogs.com/API/instance/export/participants/{event_code}"
headers = {
"User-Agent": (
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/120.0 Safari/537.36"
),
"Accept": "application/json, text/plain, */*",
"Referer": url,
"Origin": "https://www.turtlogs.com",
}
with requests.Session() as s:
r = s.get(url, headers=headers, timeout=timeout)
# one simple retry without Referer/Origin sometimes helps
if r.status_code == 403:
r = s.get(
url,
headers={**headers},
timeout=timeout,
)
r.raise_for_status()
# return r.json()
return list(set([x["name"] for x in r.json() if x["name"].isalpha()]))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment