Compare commits
4 Commits
74f476b3ed
...
dcbe90d8f2
| Author | SHA1 | Date | |
|---|---|---|---|
| dcbe90d8f2 | |||
| 18e790c6cd | |||
| ff7351d35a | |||
| 946371bcb3 |
73
get_data.py
73
get_data.py
@@ -33,7 +33,7 @@ if not os.path.exists("logs"):
|
||||
os.makedirs("logs")
|
||||
|
||||
telegram_bot_token = "7639240596:AAH0YtdQoWZSC-_R_EW4wKAHHNLIA0F_ARY"
|
||||
# TELEGRAM_CHAT_ID = 228977654
|
||||
# telegram_chat_id = 228977654
|
||||
telegram_chat_id = -4803699526
|
||||
log_config = {
|
||||
"version": 1,
|
||||
@@ -134,7 +134,13 @@ def start_offline_threads(season, game_id):
|
||||
stop_live_threads()
|
||||
|
||||
# 🔹 очищаем latest_data безопасно, чтобы не ломать структуру
|
||||
keep_keys = {"game", "pregame", "pregame-full-stats", "actual-standings", "calendar"}
|
||||
keep_keys = {
|
||||
"game",
|
||||
"pregame",
|
||||
"pregame-full-stats",
|
||||
"actual-standings",
|
||||
"calendar",
|
||||
}
|
||||
for key in list(latest_data.keys()):
|
||||
if key not in keep_keys:
|
||||
del latest_data[key]
|
||||
@@ -292,23 +298,36 @@ def stop_offline_threads():
|
||||
logger.info("[threads] OFFLINE threads stopped")
|
||||
|
||||
|
||||
|
||||
|
||||
# Функция запускаемая в потоках
|
||||
def get_data_from_API(
|
||||
name: str, url: str, quantity: float, stop_event: threading.Event
|
||||
name: str, url: str, sleep_time: float, stop_event: threading.Event
|
||||
):
|
||||
if quantity <= 0:
|
||||
raise ValueError("quantity must be > 0")
|
||||
|
||||
sleep_time = 1.0 / quantity # это и есть "раз в N секунд"
|
||||
|
||||
while not stop_event.is_set():
|
||||
start = time.time()
|
||||
try:
|
||||
value = requests.get(url, timeout=5).json()
|
||||
except json.JSONDecodeError as json_err:
|
||||
logger.warning(f"[{name}] Ошибка парсинга JSON: {json_err}")
|
||||
value = {"error": f"JSON decode error: {json_err}"}
|
||||
except requests.exceptions.Timeout:
|
||||
logger.warning(f"[{name}] Таймаут при запросе {url}")
|
||||
value = {"error": "timeout"}
|
||||
except requests.exceptions.RequestException as req_err:
|
||||
logger.warning(f"[{name}] Ошибка запроса: {req_err}")
|
||||
value = {"error": str(req_err)}
|
||||
except Exception as ex:
|
||||
logger.warning(f"[{name}] Неизвестная ошибка: {ex}")
|
||||
value = {"error": str(ex)}
|
||||
|
||||
# Проверяем, нет ли явного статуса ошибки в JSON
|
||||
if isinstance(value, dict) and str(value.get("status", "")).lower() in (
|
||||
"error",
|
||||
"fail",
|
||||
"no-status",
|
||||
):
|
||||
logger.warning(f"[{name}] API вернул статус '{value.get('status')}'")
|
||||
|
||||
ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
|
||||
results_q.put({"source": name, "ts": ts, "data": value})
|
||||
logger.debug(f"[{ts}] name: {name}, status: {value.get('status', 'no-status')}")
|
||||
@@ -372,7 +391,9 @@ def results_consumer():
|
||||
game["data"]["result"]["game"]["fullScore"] = payload["result"][
|
||||
"fullScore"
|
||||
]
|
||||
game["data"]["result"]["game"]["score"] = f'{payload["result"]["teams"][0]["total"]["points"]}:{payload["result"]["teams"][1]["total"]["points"]}'
|
||||
game["data"]["result"]["game"][
|
||||
"score"
|
||||
] = f'{payload["result"]["teams"][0]["total"]["points"]}:{payload["result"]["teams"][1]["total"]["points"]}'
|
||||
for team in game["data"]["result"]["teams"]:
|
||||
if team["teamNumber"] != 0:
|
||||
box_team = [
|
||||
@@ -460,7 +481,7 @@ def results_consumer():
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(
|
||||
"results_consumer: live-status postprocess error:", e
|
||||
f"results_consumer: live-status postprocess error: {e}"
|
||||
)
|
||||
|
||||
else:
|
||||
@@ -598,6 +619,7 @@ def extract_game_datetime(game_item: dict) -> datetime | None:
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def build_pretty_status_message():
|
||||
"""
|
||||
Собирает одно красивое сообщение про текущее состояние онлайна.
|
||||
@@ -616,7 +638,6 @@ def build_pretty_status_message():
|
||||
result = game_data.get("result") or {}
|
||||
game_info = result.get("game") or {}
|
||||
|
||||
|
||||
team1_name = result["team1"]["name"]
|
||||
team2_name = result["team2"]["name"]
|
||||
|
||||
@@ -629,9 +650,7 @@ def build_pretty_status_message():
|
||||
|
||||
if isinstance(full_score, str) and full_score:
|
||||
quarters = full_score.split(",")
|
||||
q_text = " | ".join(
|
||||
f"Q{i+1} {q}" for i, q in enumerate(quarters) if q
|
||||
)
|
||||
q_text = " | ".join(f"Q{i+1} {q}" for i, q in enumerate(quarters) if q)
|
||||
if q_text:
|
||||
lines.append(f"🧱 By quarters: {q_text}")
|
||||
|
||||
@@ -641,10 +660,7 @@ def build_pretty_status_message():
|
||||
ls = latest_data.get("live-status", {})
|
||||
ls_raw = ls.get("data") or {}
|
||||
ls_status = (
|
||||
ls_raw.get("status")
|
||||
or ls_raw.get("gameStatus")
|
||||
or ls_raw.get("state")
|
||||
or "—"
|
||||
ls_raw.get("status") or ls_raw.get("gameStatus") or ls_raw.get("state") or "—"
|
||||
)
|
||||
lines.append(f"🟢 LIVE status: <b>{ls_status}</b>")
|
||||
|
||||
@@ -713,7 +729,6 @@ def status_broadcaster():
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
global STATUS, GAME_ID, SEASON, GAME_START_DT, GAME_TODAY, GAME_SOON
|
||||
@@ -761,7 +776,7 @@ async def lifespan(app: FastAPI):
|
||||
daemon=True,
|
||||
)
|
||||
thread_result_consumer.start()
|
||||
|
||||
|
||||
thread_status_broadcaster = threading.Thread(
|
||||
target=status_broadcaster,
|
||||
daemon=True,
|
||||
@@ -777,7 +792,7 @@ async def lifespan(app: FastAPI):
|
||||
URLS["pregame"].format(
|
||||
host=HOST, league=LEAGUE, season=season, game_id=game_id, lang=LANG
|
||||
),
|
||||
0.0016667,
|
||||
60,
|
||||
stop_event,
|
||||
),
|
||||
daemon=True,
|
||||
@@ -789,7 +804,7 @@ async def lifespan(app: FastAPI):
|
||||
URLS["pregame-full-stats"].format(
|
||||
host=HOST, league=LEAGUE, season=season, game_id=game_id, lang=LANG
|
||||
),
|
||||
0.0016667,
|
||||
600,
|
||||
stop_event,
|
||||
),
|
||||
daemon=True,
|
||||
@@ -801,7 +816,7 @@ async def lifespan(app: FastAPI):
|
||||
URLS["actual-standings"].format(
|
||||
host=HOST, league=LEAGUE, season=season, lang=LANG
|
||||
),
|
||||
0.0016667,
|
||||
300,
|
||||
stop_event,
|
||||
),
|
||||
daemon=True,
|
||||
@@ -811,7 +826,7 @@ async def lifespan(app: FastAPI):
|
||||
args=(
|
||||
"game",
|
||||
URLS["game"].format(host=HOST, game_id=game_id, lang=LANG),
|
||||
0.00016,
|
||||
300,
|
||||
stop_event,
|
||||
),
|
||||
daemon=True,
|
||||
@@ -821,7 +836,7 @@ async def lifespan(app: FastAPI):
|
||||
args=(
|
||||
"live-status",
|
||||
URLS["live-status"].format(host=HOST, game_id=game_id),
|
||||
1,
|
||||
0.5,
|
||||
stop_event,
|
||||
),
|
||||
daemon=True,
|
||||
@@ -831,7 +846,7 @@ async def lifespan(app: FastAPI):
|
||||
args=(
|
||||
"box-score",
|
||||
URLS["box-score"].format(host=HOST, game_id=game_id),
|
||||
1,
|
||||
0.5,
|
||||
stop_event,
|
||||
),
|
||||
daemon=True,
|
||||
@@ -853,7 +868,7 @@ async def lifespan(app: FastAPI):
|
||||
args=(
|
||||
"game",
|
||||
URLS["game"].format(host=HOST, game_id=game_id, lang=LANG),
|
||||
1, # реже
|
||||
300, # реже
|
||||
stop_event,
|
||||
),
|
||||
daemon=True,
|
||||
@@ -967,6 +982,7 @@ async def game():
|
||||
|
||||
@app.get("/status")
|
||||
async def status(request: Request):
|
||||
global STATUS # будем его править, если live-status свежее
|
||||
def color_for_status(status_value: str) -> str:
|
||||
"""Подбор цвета статуса в HEX"""
|
||||
status_value = str(status_value).lower()
|
||||
@@ -1622,7 +1638,6 @@ def add_data_for_teams(new_data):
|
||||
|
||||
total_age += player["age"]
|
||||
total_height += player["height"]
|
||||
|
||||
total_points = points_start + points_bench
|
||||
points_start_pro = (
|
||||
f"{round(points_start * 100 / total_points)}%" if total_points else "0%"
|
||||
|
||||
Reference in New Issue
Block a user