Compare commits

...

3 Commits

Author SHA1 Message Date
25da1593d1
UI 2025-05-25 15:22:57 +00:00
a10fa2be5c
ruff 2025-05-24 15:12:04 +00:00
18f52a2e47
Add nuitka 2025-05-24 14:08:30 +00:00
8 changed files with 869 additions and 152 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
main.build
main.dist
main.onefile-build
main.bin
__pycache__

85
config.py Normal file
View File

@ -0,0 +1,85 @@
import tkinter as tk
import os
from pathlib import Path
import sqlite3
APP_NAME = "SC KF"
DEBUG = os.getenv("DEBUG") is not None
class PersistentConfig:
def __init__(self, db_path: Path):
self.table_name = "properties"
self.db = sqlite3.connect(
str(db_path), isolation_level=None, check_same_thread=False, autocommit=True
)
self.db.execute("pragma journal_mode=wal;")
self.db.execute(
f"create table if not exists {self.table_name} (key text primary key, value_str text, value_int int) strict;"
)
def set_int(self, key: str, value: int) -> None:
self.db.execute(
f"insert into {self.table_name} (key, value_int) values (:key, :value) on conflict (key) do update set value_int = :value;",
{"key": key, "value": value},
)
def get_int(self, key: str, default=None) -> int | None:
res = self.db.execute(
f"select value_int from {self.table_name} where key = ?;", (key,)
)
return next(iter(res), (default,))[0]
def set_str(self, key: str, value: str) -> None:
self.db.execute(
f"insert into {self.table_name} (key, value_str) values (:key, :value) on conflict (key) do update set value_str = :value;",
{"key": key, "value": value},
)
def get_str(self, key: str, default=None) -> str | None:
res = self.db.execute(
f"select value_str from {self.table_name} where key = ?;", (key,)
)
return next(iter(res), (default,))[0]
def set_bool(self, key: str, value: bool) -> None:
self.set_int(key, int(value))
def get_bool(self, key: str, default=None) -> bool | None:
return bool(self.get_int(key, default))
def get_tk_boolean(self, name: str, default: bool) -> tk.BooleanVar:
value = self.get_bool(name, default)
var = tk.BooleanVar(name=name, value=value)
var.trace_add("write", lambda a, b, c: self.set_bool(name, var.get()))
return var
def get_tk_str(self, name: str, default: str | None) -> tk.StringVar:
value = self.get_str(name, default)
var = tk.StringVar(name=name, value=value)
var.trace_add("write", lambda a, b, c: self.set_str(name, var.get()))
return var
def get_tk_int(self, name: str, default: int | None) -> tk.IntVar:
value = self.get_int(name, default)
var = tk.IntVar(name=name, value=value)
var.trace_add("write", lambda a, b, c: self.set_int(name, var.get()))
return var
def get_app_data_dir() -> Path:
if os.name == "nt":
directory = Path.home() / "AppData/Roaming/SCKF"
elif os.name == "posix":
directory = Path.home() / ".config/SCKF"
else:
raise RuntimeError(f"Unsupported os: {os.name}")
directory.mkdir(exist_ok=True, parents=True)
return directory
db = PersistentConfig(get_app_data_dir() / "config.sqlite")

27
discord_subscriber.py Normal file
View File

@ -0,0 +1,27 @@
import requests
from monitor import Event, SubscriberABC
from loguru import logger
class DiscordSubscriber(SubscriberABC):
def __init__(self):
self.webhook_url: str | None = None
self.enabled: bool = True
def set_url(self, webhook_url: str) -> None:
self.webhook_url = webhook_url
def set_enabled(self, value: bool) -> None:
self.enabled = value
def consume_event(self, event: Event) -> None:
if not self.enabled:
return
try:
requests.post(
self.webhook_url, json={"content": event.to_str(include_ts=False)}
)
except Exception as e:
logger.opt(exception=True).warning(f"Error sending to Discord: {event}")

156
main.py
View File

@ -1,157 +1,11 @@
from pathlib import Path
import requests
import time
from dataclasses import dataclass
from typing import Generator, Self
DISCORD_WEBHOOK_URL = "HOOOOOOOOOOOOOOOOK"
LOG_FILE_PATH = Path(r"E:\RSI\StarCitizen\LIVE\Game.log")
INCLUDE_NPC = False
ONLY_PERSONAL_FEED = True
DEBUG = False
damage_type_to_emoji = {
"ElectricArc": ":cloud_lightning:",
"Explosion": ":boom:",
"VehicleDestruction": ":boom: :airplane:",
"TakeDown": ":martial_arts_uniform: :face_with_peeking_eye:",
}
@dataclass(frozen=True)
class Line:
line: str
is_realtime: bool
@dataclass(frozen=True)
class Kill:
killer: str
victim: str
zone: str
killed_by: str
damage_type: str
@staticmethod
def from_line(line: str) -> "Self":
# <2025-05-23T16:48:53.672Z> [Notice] <Actor Death> CActor::Kill: 'Neon0ne' [1895570109956] in zone 'ANVL_Ballista_3700492224326' killed by 'Aleksey31' [1978962253870] using 'MRCK_S07_ANVL_Ballista_Duel_3700492224372' [Class unknown] with damage type 'VehicleDestruction' from direction x: 0.000000, y: 0.000000, z: 0.000000 [Team_ActorTech][Actor]
splited = [i.strip("'") for i in line.split()]
return Kill(
victim=splited[5],
zone=splited[9],
killer=splited[12],
killed_by=splited[15],
damage_type=splited[21],
)
class App:
def __init__(
self,
log_file_path: Path,
discord_webhook_url: str,
do_backread: bool = False,
do_mock_discord: bool = False,
):
self.log_file_path = log_file_path
self.do_backread: bool = do_backread
self.do_mock_discord: bool = do_mock_discord
self.discord_webhook_url: str = discord_webhook_url
self.nickname: str | None = None
if not (discord_webhook_url.startswith("https://") or do_mock_discord):
msg = f'{discord_webhook_url} не вебхук, так то'
print(msg)
raise RuntimeError(msg)
def try_set_self_name(self, line: str) -> None:
# <2025-05-23T16:19:10.244Z> [Notice] <AccountLoginCharacterStatus_Character> Character: createdAt 1747220301029 - updatedAt 1747220301703 - geid 1978962253870 - accountId 2261831 - name Aleksey31 - state STATE_CURRENT [Team_GameServices][Login]
if self.nickname is not None:
print("Warning, AccountLoginCharacterStatus_Character duplicate", line)
return
try:
self.nickname = line.split()[17]
print(f"Extracted {self.nickname=}")
except IndexError:
print("Failed to extract nickname from line", line)
def process_lines(self) -> None:
for line in self.monitor_log_file():
process_important_realtime_events = self.do_backread or line.is_realtime
if "<AccountLoginCharacterStatus_Character>" in line.line:
self.try_set_self_name(line.line)
continue
if (
"[Notice] <Actor Death> CActor::Kill:" in line.line
and process_important_realtime_events
):
kill = Kill.from_line(line.line)
if '_NPC_' in kill.killer + kill.victim and not INCLUDE_NPC:
continue
if self.nickname not in (kill.killer, kill.victim) and ONLY_PERSONAL_FEED is True:
continue
self.send_kill(kill)
def send_kill(self, kill: Kill) -> None:
if kill.victim == kill.killer:
msg = f"**{kill.victim}** РКН 🤦"
else:
emoji = damage_type_to_emoji.get(kill.damage_type, "")
msg = f"**{kill.victim}** killed by 🔫 **{kill.killer}** {emoji}"
self.send(msg)
def send(self, message: str) -> None:
try:
if self.do_mock_discord:
print("Sending message to discord", message)
else:
requests.post(self.discord_webhook_url, json={"content": message})
except Exception as e:
print(f"Error sending to Discord: {e}")
def monitor_log_file(self) -> Generator[Line, None, None]:
is_realtime = False
with self.log_file_path.open("r", encoding="utf-8") as file:
while True:
current_position = file.tell()
line = file.readline()
if not line:
is_realtime = True
time.sleep(5)
file.seek(current_position)
if DEBUG is True:
return
line = line.removesuffix("\n")
yield Line(line, is_realtime)
from ui import DeathLogGUI
import tkinter as tk
def main():
try:
if DEBUG is True:
app = App(Path("./Game.log"), DISCORD_WEBHOOK_URL, do_mock_discord=True, do_backread=True)
else:
app = App(LOG_FILE_PATH, DISCORD_WEBHOOK_URL)
app.process_lines()
except KeyboardInterrupt:
print("\nMonitoring stopped")
root = tk.Tk()
app = DeathLogGUI(root)
root.mainloop()
if __name__ == "__main__":

182
monitor.py Normal file
View File

@ -0,0 +1,182 @@
import datetime
from pathlib import Path
import time
from dataclasses import dataclass
from typing import Generator, Self
import config
from loguru import logger
from abc import ABC, abstractmethod
def parse_timestamp(line: str) -> datetime.datetime | None:
# <2025-05-23T16:48:53.672Z>
try:
line = line.strip("<>").replace("Z", "+00:00")
return datetime.datetime.fromisoformat(line)
except Exception:
logger.warning(f"Failed to parse timestamp from {line!r}")
return None
damage_type_to_emoji = {
"ElectricArc": ":cloud_lightning:",
"Explosion": ":boom:",
"VehicleDestruction": ":boom: :airplane:",
"TakeDown": ":martial_arts_uniform: :face_with_peeking_eye:",
}
@dataclass(frozen=True)
class Line:
line: str
is_realtime: bool
@dataclass(frozen=True)
class Event:
timestamp: datetime.datetime | None
def to_str(self, include_ts: bool = False) -> str:
return str(self)
class SubscriberABC(ABC):
@abstractmethod
def consume_event(self, event: Event) -> None:
raise NotImplementedError
@dataclass(frozen=True)
class Kill(Event):
killer: str
victim: str
zone: str
killed_by: str
damage_type: str
@staticmethod
def from_line(line: str) -> "Self":
# <2025-05-23T16:48:53.672Z> [Notice] <Actor Death> CActor::Kill: 'Neon0ne' [1895570109956] in zone 'ANVL_Ballista_3700492224326' killed by 'Aleksey31' [1978962253870] using 'MRCK_S07_ANVL_Ballista_Duel_3700492224372' [Class unknown] with damage type 'VehicleDestruction' from direction x: 0.000000, y: 0.000000, z: 0.000000 [Team_ActorTech][Actor]
splited = [i.strip("'") for i in line.split()]
return Kill(
timestamp=parse_timestamp(splited[0]),
victim=splited[5],
zone=splited[9],
killer=splited[12],
killed_by=splited[15],
damage_type=splited[21],
)
def to_str(self, include_ts: bool = False) -> str:
ts = (self.timestamp.strftime("%Y-%m-%d %H:%M:%S") + ": ") * include_ts
if self.victim == self.killer:
return f"{ts}**{self.victim}** РКН 🤦"
emoji = damage_type_to_emoji.get(self.damage_type, "")
return f"{ts}**{self.victim}** killed by 🔫 **{self.killer}** {emoji}"
@dataclass
class Monitor:
log_file_path: Path
subscribers: set[SubscriberABC]
do_backread: bool = False
nickname: str | None = None
running: bool = False
def try_set_self_name(self, line: str) -> None:
# <2025-05-23T16:19:10.244Z> [Notice] <AccountLoginCharacterStatus_Character> Character: createdAt 1747220301029 - updatedAt 1747220301703 - geid 1978962253870 - accountId 2261831 - name Aleksey31 - state STATE_CURRENT [Team_GameServices][Login]
if self.nickname is not None:
logger.warning(
"Warning, AccountLoginCharacterStatus_Character duplicate", line
)
return
try:
self.nickname = line.split()[17]
logger.info(f"Extracted {self.nickname=}")
except IndexError:
logger.warning("Failed to extract nickname from line", line)
def call_subscribers(self, event: Event) -> None:
logger.info(f"Passing event: {event}")
if len(self.subscribers) == 0:
logger.warning("set of subscribers is empty")
for subscriber in self.subscribers:
subscriber.consume_event(event)
def process_lines(self) -> None:
logger.info("Beginning lines consuming")
self.running = True
for line in self.monitor_log_file():
process_important_realtime_events = self.do_backread or line.is_realtime
if "<AccountLoginCharacterStatus_Character>" in line.line:
self.try_set_self_name(line.line)
continue
if (
"[Notice] <Actor Death> CActor::Kill:" in line.line
and process_important_realtime_events
):
kill = Kill.from_line(line.line)
# if "_NPC_" in kill.killer + kill.victim and not self.include_npc: # TODO:
# continue
#
# if (
# self.nickname not in (kill.killer, kill.victim)
# and self.only_personal_feed is True
# ):
# continue
self.call_subscribers(kill)
def monitor_log_file(self) -> Generator[Line, None, None]:
is_realtime = False
with self.log_file_path.open("r", encoding="utf-8") as file:
while self.running:
current_position = file.tell()
line = file.readline()
if not line:
if not is_realtime:
logger.info("Reached edge of the file")
is_realtime = True
time.sleep(0.15)
file.seek(current_position)
if config.DEBUG is True:
return
line = line.removesuffix("\n")
yield Line(line, is_realtime)
logger.info("Exiting reading loop")
# def main():
# try:
# if config.DEBUG is True:
# app = Monitor(
# log_file_path=Path("./Game.log"),
# do_backread=True,
# )
#
# else:
# app = Monitor(config.LOG_FILE_PATH, config.DISCORD_WEBHOOK_URL)
#
# app.process_lines()
#
# except KeyboardInterrupt:
# print("\nMonitoring stopped")
#
#
# if __name__ == "__main__":
# main()

View File

@ -4,5 +4,13 @@ version = "0.1.0"
description = "Add your description here"
requires-python = ">=3.13"
dependencies = [
"loguru>=0.7.3",
"pystray>=0.19.5",
"requests>=2.32.3",
"sv-ttk>=2.6.0",
]
[dependency-groups]
dev = [
"nuitka>=2.7.3",
]

320
ui.py Normal file
View File

@ -0,0 +1,320 @@
from functools import partial
from collections import deque
import pystray
from pystray import Menu, MenuItem
from PIL import Image, ImageDraw
import config
import threading
from loguru import logger
import sv_ttk
from pathlib import Path
import threading
import tkinter as tk
from tkinter import ttk, filedialog, scrolledtext
import config
from discord_subscriber import DiscordSubscriber
from monitor import Monitor, Event, SubscriberABC
from loguru import logger
from typing import Callable
class Feed(SubscriberABC):
def __init__(self, callback_data_changed: Callable | None = None):
self.ring_buffer: deque[Event] = deque(maxlen=150)
self.ring_buffer_lock = threading.Lock()
self.callback = callback_data_changed
self.trace_callback = lambda name, b, c: self._notify_data_changed()
self.include_ts: tk.BooleanVar = config.db.get_tk_boolean("INCLUDE_TS", True)
self.include_ts.trace_add("write", self.trace_callback)
def consume_event(self, event: Event) -> None:
with self.ring_buffer_lock:
self.ring_buffer.append(event)
self._notify_data_changed()
def render(self) -> str:
with self.ring_buffer_lock:
return "\n".join(
event.to_str(include_ts=self.include_ts.get())
for event in self.ring_buffer
)
def clear(self) -> None:
with self.ring_buffer_lock:
self.ring_buffer.clear()
self._notify_data_changed()
def _notify_data_changed(self) -> None:
if callable(self.callback):
self.callback()
def get_image(
width: int = 64, height: int = 64, color1: str = "black", color2: str = "green"
) -> Image:
image = Image.new("RGB", (width, height), color1)
dc = ImageDraw.Draw(image)
dc.rectangle((width // 2, 0, width, height // 2), fill=color2)
dc.rectangle((0, height // 2, width // 2, height), fill=color2)
return image
class DeathLogGUI:
def __init__(self, root: tk.Tk):
self.root = root
self.root.title(config.APP_NAME)
self.root.geometry("850x600")
self.root.minsize(850, 600)
self.root.protocol("WM_DELETE_WINDOW", self.handle_close_window)
theme_mapping = {True: "dark", False: "light"}
sv_ttk.set_theme(theme_mapping.get(config.db.get_bool("IS_DARK_MODE", False)))
self.log_path = config.db.get_tk_str(
"LOG_FILE_PATH", r"E:\RSI\StarCitizen\LIVE\Game.log"
)
self.do_backread = config.db.get_tk_boolean("DO_BACKREAD", False)
self.include_npc_kills = config.db.get_tk_boolean("INCLUDE_NPC_KILLS", True)
self.is_dark_mode = config.db.get_tk_boolean("IS_DARK_MODE", False)
self.is_dark_mode.trace_add(
"write",
lambda a, b, c: sv_ttk.set_theme(
theme_mapping.get(self.is_dark_mode.get()), self.root
),
)
self.feed = Feed(
callback_data_changed=partial(self.root.after, 0, self.update_feed_display)
)
self.enable_discord = config.db.get_tk_boolean("ENABLE_DISCORD", False)
self.webhook_url = config.db.get_tk_str(
"DISCORD_WEBHOOK_URL", "https://discord.com/..."
)
self.discord = DiscordSubscriber()
self.discord.enabled = self.enable_discord.get()
self.discord.webhook_url = self.webhook_url.get()
self.webhook_url.trace_add(
"write", lambda a, b, c: self.discord.set_url(self.webhook_url.get())
)
self.enable_discord.trace_add(
"read", lambda a, b, c: self.discord.set_enabled(self.enable_discord.get())
)
self.monitor: Monitor | None = None
self.monitor_thread: threading.Thread | None = None
self.create_widgets()
self.update_feed_display()
self.tray = pystray.Icon(
name=config.APP_NAME,
icon=get_image(),
menu=Menu(
MenuItem("Show window", self.handle_open_window, default=True),
MenuItem("Exit", self.shutdown),
),
)
self.tray.visible = False
self.tray_thread = threading.Thread(
target=self.tray.run, kwargs={"setup": lambda _: None}
)
self.tray_thread.start()
def shutdown(self):
logger.info("Beginning GUI shutdown")
self.stop_monitoring()
self.tray.stop()
self.root.quit()
def handle_close_window(self) -> None:
self.root.withdraw()
self.tray.visible = True
def handle_open_window(self) -> None:
self.root.deiconify()
self.tray.visible = False
def create_widgets(self):
# Create main frame
main_frame = ttk.Frame(self.root, padding="10")
main_frame.pack(fill=tk.BOTH, expand=True)
# Configuration section
config_frame = ttk.LabelFrame(main_frame, text="Configuration", padding="10")
config_frame.pack(fill=tk.X, pady=5)
# Log file path
ttk.Label(config_frame, text="Log File Path:").grid(
row=0, column=0, sticky=tk.W, pady=2
)
ttk.Entry(config_frame, textvariable=self.log_path, width=50).grid(
row=0, column=1, sticky=tk.W + tk.E, pady=2, padx=5
)
ttk.Button(config_frame, text="Browse", command=self.browse_log_file).grid(
row=0, column=2, pady=2
)
# Discord webhook URL
ttk.Label(config_frame, text="Discord Webhook URL:").grid(
row=1, column=0, sticky=tk.W, pady=2
)
ttk.Entry(config_frame, textvariable=self.webhook_url, width=50).grid(
row=1, column=1, sticky=tk.W + tk.E, pady=2, padx=5
)
# Checkboxes
ttk.Checkbutton(
config_frame, text="Process existing log entries", variable=self.do_backread
).grid(row=2, column=0, columnspan=2, sticky=tk.W, pady=2)
ttk.Checkbutton(
config_frame,
text="Do send events to discord",
variable=self.enable_discord,
).grid(row=3, column=0, columnspan=2, sticky=tk.W, pady=2)
ttk.Checkbutton(
config_frame,
text="Hide NPC kills (_NPC_ in name)",
variable=self.include_npc_kills,
).grid(row=4, column=0, columnspan=2, sticky=tk.W, pady=2)
ttk.Checkbutton(
config_frame,
text="Include timestamps",
variable=self.feed.include_ts,
).grid(row=5, column=0, sticky=tk.W, pady=2)
ttk.Checkbutton(
config_frame,
text="Enable dark mode",
variable=self.is_dark_mode,
).grid(row=6, column=0, sticky=tk.W, pady=2)
# Control buttons
control_frame = ttk.Frame(main_frame)
control_frame.pack(fill=tk.X, pady=5)
self.start_button = ttk.Button(
control_frame, text="Start Monitoring", command=self.start_monitoring
)
self.start_button.pack(side=tk.LEFT, padx=5)
self.stop_button = ttk.Button(
control_frame,
text="Stop Monitoring",
command=self.stop_monitoring,
state=tk.DISABLED,
)
self.stop_button.pack(side=tk.LEFT, padx=5)
# Status indicator
self.status_var = tk.StringVar(value="Not monitoring")
status_frame = ttk.Frame(main_frame)
status_frame.pack(fill=tk.X, pady=5)
ttk.Label(status_frame, text="Status:").pack(side=tk.LEFT)
self.status_label = ttk.Label(
status_frame, textvariable=self.status_var, foreground="red"
)
self.status_label.pack(side=tk.LEFT, padx=5)
kills_frame = ttk.LabelFrame(
main_frame, text="Recent Kill Events", padding="10"
)
kills_frame.pack(fill=tk.BOTH, expand=True, pady=5)
self.feed_display = scrolledtext.ScrolledText(
kills_frame, wrap=tk.WORD, height=10
)
self.feed_display.pack(fill=tk.BOTH, expand=True)
self.feed_display.config(state=tk.DISABLED)
def browse_log_file(self):
filename = filedialog.askopenfilename(
title="Select Star Citizen Log File",
filetypes=[("Log Files", "*.log"), ("All Files", "*.*")],
)
if filename:
self.log_path.set(filename)
def start_monitoring(self):
if self.monitor_thread and self.monitor_thread.is_alive():
return
try:
self.monitor = Monitor(
log_file_path=Path(self.log_path.get()),
do_backread=self.do_backread.get(),
subscribers={self.feed},
)
self.monitor_thread = threading.Thread(
target=self.monitor.process_lines, daemon=True
)
self.monitor_thread.start()
self.feed.clear()
self.status_var.set("Monitoring active")
self.status_label.config(foreground="green")
self.start_button.config(state=tk.DISABLED)
self.stop_button.config(state=tk.NORMAL)
except Exception as e:
self.status_var.set(f"Error: {str(e)}")
self.status_label.config(foreground="red")
logger.opt(exception=True).exception("Failed to start monitoring")
def stop_monitoring(self):
if self.monitor:
self.monitor.running = False
self.monitor_thread.join(timeout=1.0)
self.status_var.set("Monitoring stopped")
self.status_label.config(foreground="red")
self.start_button.config(state=tk.NORMAL)
self.stop_button.config(state=tk.DISABLED)
def update_feed_display(self):
self.feed_display.config(state=tk.NORMAL)
self.feed_display.delete("1.0", tk.END)
self.feed_display.insert("1.0", self.feed.render())
self.feed_display.config(state=tk.DISABLED)
self.feed_display.see(tk.END)
# try:
# # Check if we should run in GUI mode or CLI mode
# import sys
#
# # CLI mode
# if config.DEBUG is True:
# app = App(
# Path("./Game.log"),
# DEFAULT_DISCORD_WEBHOOK_URL,
# do_mock_discord=True,
# do_backread=True,
# hide_npc_kills=False,
# )
#
# else:
# app = App(
# DEFAULT_LOG_FILE_PATH,
# DEFAULT_DISCORD_WEBHOOK_URL,
# hide_npc_kills=False,
# )
# app.process_lines()
#
# else:
# # GUI mode
# root = tk.Tk()
# app = DeathLogGUI(root)
# root.mainloop()
#
# except KeyboardInterrupt:
# print("\nMonitoring stopped")

237
uv.lock generated
View File

@ -11,6 +11,28 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618 },
]
[[package]]
name = "cffi"
version = "1.17.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pycparser" },
]
sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989 },
{ url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802 },
{ url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792 },
{ url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893 },
{ url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810 },
{ url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200 },
{ url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447 },
{ url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358 },
{ url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469 },
{ url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475 },
{ url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009 },
]
[[package]]
name = "charset-normalizer"
version = "3.4.2"
@ -33,6 +55,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626 },
]
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
]
[[package]]
name = "idna"
version = "3.10"
@ -42,6 +73,140 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
]
[[package]]
name = "loguru"
version = "0.7.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "win32-setctime", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/3a/05/a1dae3dffd1116099471c643b8924f5aa6524411dc6c63fdae648c4f1aca/loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6", size = 63559 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595 },
]
[[package]]
name = "nuitka"
version = "2.7.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "ordered-set" },
{ name = "zstandard" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d6/40/7fd16fffa8bbf78f9f9b1abe75ac123e3cc46740c6142c4db4e29b32ce4a/Nuitka-2.7.3.tar.gz", hash = "sha256:bd3dd12c5f0dfec0208f88c14ef3bbfc4ba3495eafc62c31b4e1f71171536e47", size = 3884010 }
[[package]]
name = "ordered-set"
version = "4.1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/4c/ca/bfac8bc689799bcca4157e0e0ced07e70ce125193fc2e166d2e685b7e2fe/ordered-set-4.1.0.tar.gz", hash = "sha256:694a8e44c87657c59292ede72891eb91d34131f6531463aab3009191c77364a8", size = 12826 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/33/55/af02708f230eb77084a299d7b08175cff006dea4f2721074b92cdb0296c0/ordered_set-4.1.0-py3-none-any.whl", hash = "sha256:046e1132c71fcf3330438a539928932caf51ddbc582496833e23de611de14562", size = 7634 },
]
[[package]]
name = "pillow"
version = "11.2.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/af/cb/bb5c01fcd2a69335b86c22142b2bccfc3464087efb7fd382eee5ffc7fdf7/pillow-11.2.1.tar.gz", hash = "sha256:a64dd61998416367b7ef979b73d3a85853ba9bec4c2925f74e588879a58716b6", size = 47026707 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/36/9c/447528ee3776e7ab8897fe33697a7ff3f0475bb490c5ac1456a03dc57956/pillow-11.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fdec757fea0b793056419bca3e9932eb2b0ceec90ef4813ea4c1e072c389eb28", size = 3190098 },
{ url = "https://files.pythonhosted.org/packages/b5/09/29d5cd052f7566a63e5b506fac9c60526e9ecc553825551333e1e18a4858/pillow-11.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0e130705d568e2f43a17bcbe74d90958e8a16263868a12c3e0d9c8162690830", size = 3030166 },
{ url = "https://files.pythonhosted.org/packages/71/5d/446ee132ad35e7600652133f9c2840b4799bbd8e4adba881284860da0a36/pillow-11.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bdb5e09068332578214cadd9c05e3d64d99e0e87591be22a324bdbc18925be0", size = 4408674 },
{ url = "https://files.pythonhosted.org/packages/69/5f/cbe509c0ddf91cc3a03bbacf40e5c2339c4912d16458fcb797bb47bcb269/pillow-11.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d189ba1bebfbc0c0e529159631ec72bb9e9bc041f01ec6d3233d6d82eb823bc1", size = 4496005 },
{ url = "https://files.pythonhosted.org/packages/f9/b3/dd4338d8fb8a5f312021f2977fb8198a1184893f9b00b02b75d565c33b51/pillow-11.2.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:191955c55d8a712fab8934a42bfefbf99dd0b5875078240943f913bb66d46d9f", size = 4518707 },
{ url = "https://files.pythonhosted.org/packages/13/eb/2552ecebc0b887f539111c2cd241f538b8ff5891b8903dfe672e997529be/pillow-11.2.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:ad275964d52e2243430472fc5d2c2334b4fc3ff9c16cb0a19254e25efa03a155", size = 4610008 },
{ url = "https://files.pythonhosted.org/packages/72/d1/924ce51bea494cb6e7959522d69d7b1c7e74f6821d84c63c3dc430cbbf3b/pillow-11.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:750f96efe0597382660d8b53e90dd1dd44568a8edb51cb7f9d5d918b80d4de14", size = 4585420 },
{ url = "https://files.pythonhosted.org/packages/43/ab/8f81312d255d713b99ca37479a4cb4b0f48195e530cdc1611990eb8fd04b/pillow-11.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fe15238d3798788d00716637b3d4e7bb6bde18b26e5d08335a96e88564a36b6b", size = 4667655 },
{ url = "https://files.pythonhosted.org/packages/94/86/8f2e9d2dc3d308dfd137a07fe1cc478df0a23d42a6c4093b087e738e4827/pillow-11.2.1-cp313-cp313-win32.whl", hash = "sha256:3fe735ced9a607fee4f481423a9c36701a39719252a9bb251679635f99d0f7d2", size = 2332329 },
{ url = "https://files.pythonhosted.org/packages/6d/ec/1179083b8d6067a613e4d595359b5fdea65d0a3b7ad623fee906e1b3c4d2/pillow-11.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:74ee3d7ecb3f3c05459ba95eed5efa28d6092d751ce9bf20e3e253a4e497e691", size = 2676388 },
{ url = "https://files.pythonhosted.org/packages/23/f1/2fc1e1e294de897df39fa8622d829b8828ddad938b0eaea256d65b84dd72/pillow-11.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:5119225c622403afb4b44bad4c1ca6c1f98eed79db8d3bc6e4e160fc6339d66c", size = 2414950 },
{ url = "https://files.pythonhosted.org/packages/c4/3e/c328c48b3f0ead7bab765a84b4977acb29f101d10e4ef57a5e3400447c03/pillow-11.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8ce2e8411c7aaef53e6bb29fe98f28cd4fbd9a1d9be2eeea434331aac0536b22", size = 3192759 },
{ url = "https://files.pythonhosted.org/packages/18/0e/1c68532d833fc8b9f404d3a642991441d9058eccd5606eab31617f29b6d4/pillow-11.2.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9ee66787e095127116d91dea2143db65c7bb1e232f617aa5957c0d9d2a3f23a7", size = 3033284 },
{ url = "https://files.pythonhosted.org/packages/b7/cb/6faf3fb1e7705fd2db74e070f3bf6f88693601b0ed8e81049a8266de4754/pillow-11.2.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9622e3b6c1d8b551b6e6f21873bdcc55762b4b2126633014cea1803368a9aa16", size = 4445826 },
{ url = "https://files.pythonhosted.org/packages/07/94/8be03d50b70ca47fb434a358919d6a8d6580f282bbb7af7e4aa40103461d/pillow-11.2.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63b5dff3a68f371ea06025a1a6966c9a1e1ee452fc8020c2cd0ea41b83e9037b", size = 4527329 },
{ url = "https://files.pythonhosted.org/packages/fd/a4/bfe78777076dc405e3bd2080bc32da5ab3945b5a25dc5d8acaa9de64a162/pillow-11.2.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:31df6e2d3d8fc99f993fd253e97fae451a8db2e7207acf97859732273e108406", size = 4549049 },
{ url = "https://files.pythonhosted.org/packages/65/4d/eaf9068dc687c24979e977ce5677e253624bd8b616b286f543f0c1b91662/pillow-11.2.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:062b7a42d672c45a70fa1f8b43d1d38ff76b63421cbbe7f88146b39e8a558d91", size = 4635408 },
{ url = "https://files.pythonhosted.org/packages/1d/26/0fd443365d9c63bc79feb219f97d935cd4b93af28353cba78d8e77b61719/pillow-11.2.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4eb92eca2711ef8be42fd3f67533765d9fd043b8c80db204f16c8ea62ee1a751", size = 4614863 },
{ url = "https://files.pythonhosted.org/packages/49/65/dca4d2506be482c2c6641cacdba5c602bc76d8ceb618fd37de855653a419/pillow-11.2.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f91ebf30830a48c825590aede79376cb40f110b387c17ee9bd59932c961044f9", size = 4692938 },
{ url = "https://files.pythonhosted.org/packages/b3/92/1ca0c3f09233bd7decf8f7105a1c4e3162fb9142128c74adad0fb361b7eb/pillow-11.2.1-cp313-cp313t-win32.whl", hash = "sha256:e0b55f27f584ed623221cfe995c912c61606be8513bfa0e07d2c674b4516d9dd", size = 2335774 },
{ url = "https://files.pythonhosted.org/packages/a5/ac/77525347cb43b83ae905ffe257bbe2cc6fd23acb9796639a1f56aa59d191/pillow-11.2.1-cp313-cp313t-win_amd64.whl", hash = "sha256:36d6b82164c39ce5482f649b437382c0fb2395eabc1e2b1702a6deb8ad647d6e", size = 2681895 },
{ url = "https://files.pythonhosted.org/packages/67/32/32dc030cfa91ca0fc52baebbba2e009bb001122a1daa8b6a79ad830b38d3/pillow-11.2.1-cp313-cp313t-win_arm64.whl", hash = "sha256:225c832a13326e34f212d2072982bb1adb210e0cc0b153e688743018c94a2681", size = 2417234 },
]
[[package]]
name = "pycparser"
version = "2.22"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552 },
]
[[package]]
name = "pyobjc-core"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/5c/94/a111239b98260869780a5767e5d74bfd3a8c13a40457f479c28dcd91f89d/pyobjc_core-11.0.tar.gz", hash = "sha256:63bced211cb8a8fb5c8ff46473603da30e51112861bd02c438fbbbc8578d9a70", size = 994931 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/72/16/0c468e73dbecb821e3da8819236fe832dfc53eb5f66a11775b055a7589ea/pyobjc_core-11.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c338c1deb7ab2e9436d4175d1127da2eeed4a1b564b3d83b9f3ae4844ba97e86", size = 743900 },
{ url = "https://files.pythonhosted.org/packages/f3/88/cecec88fd51f62a6cd7775cc4fb6bfde16652f97df88d28c84fb77ca0c18/pyobjc_core-11.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b4e9dc4296110f251a4033ff3f40320b35873ea7f876bd29a1c9705bb5e08c59", size = 791905 },
]
[[package]]
name = "pyobjc-framework-cocoa"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pyobjc-core" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c5/32/53809096ad5fc3e7a2c5ddea642590a5f2cb5b81d0ad6ea67fdb2263d9f9/pyobjc_framework_cocoa-11.0.tar.gz", hash = "sha256:00346a8cb81ad7b017b32ff7bf596000f9faa905807b1bd234644ebd47f692c5", size = 6173848 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1d/a5/609281a7e89efefbef9db1d8fe66bc0458c3b4e74e2227c644f9c18926fa/pyobjc_framework_Cocoa-11.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:15b2bd977ed340074f930f1330f03d42912d5882b697d78bd06f8ebe263ef92e", size = 385889 },
{ url = "https://files.pythonhosted.org/packages/93/f6/2d5a863673ef7b85a3cba875c43e6c495fb1307427a6801001ae94bb5e54/pyobjc_framework_Cocoa-11.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:5750001db544e67f2b66f02067d8f0da96bb2ef71732bde104f01b8628f9d7ea", size = 389831 },
]
[[package]]
name = "pyobjc-framework-quartz"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pyobjc-core" },
{ name = "pyobjc-framework-cocoa" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a5/ad/f00f3f53387c23bbf4e0bb1410e11978cbf87c82fa6baff0ee86f74c5fb6/pyobjc_framework_quartz-11.0.tar.gz", hash = "sha256:3205bf7795fb9ae34747f701486b3db6dfac71924894d1f372977c4d70c3c619", size = 3952463 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a6/9e/54c48fe8faab06ee5eb80796c8c17ec61fc313d84398540ee70abeaf7070/pyobjc_framework_Quartz-11.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:973b4f9b8ab844574461a038bd5269f425a7368d6e677e3cc81fcc9b27b65498", size = 212478 },
{ url = "https://files.pythonhosted.org/packages/4a/28/456b54a59bfe11a91b7b4e94f8ffdcf174ffd1efa169f4283e5b3bc10194/pyobjc_framework_Quartz-11.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:66ab58d65348863b8707e63b2ec5cdc54569ee8189d1af90d52f29f5fdf6272c", size = 217973 },
]
[[package]]
name = "pystray"
version = "0.19.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pillow" },
{ name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" },
{ name = "python-xlib", marker = "sys_platform == 'linux'" },
{ name = "six" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/5c/64/927a4b9024196a4799eba0180e0ca31568426f258a4a5c90f87a97f51d28/pystray-0.19.5-py2.py3-none-any.whl", hash = "sha256:a0c2229d02cf87207297c22d86ffc57c86c227517b038c0d3c59df79295ac617", size = 49068 },
]
[[package]]
name = "python-xlib"
version = "0.33"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "six" },
]
sdist = { url = "https://files.pythonhosted.org/packages/86/f5/8c0653e5bb54e0cbdfe27bf32d41f27bc4e12faa8742778c17f2a71be2c0/python-xlib-0.33.tar.gz", hash = "sha256:55af7906a2c75ce6cb280a584776080602444f75815a7aff4d287bb2d7018b32", size = 269068 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/fc/b8/ff33610932e0ee81ae7f1269c890f697d56ff74b9f5b2ee5d9b7fa2c5355/python_xlib-0.33-py2.py3-none-any.whl", hash = "sha256:c3534038d42e0df2f1392a1b30a15a4ff5fdc2b86cfa94f072bf11b10a164398", size = 182185 },
]
[[package]]
name = "requests"
version = "2.32.3"
@ -62,11 +227,45 @@ name = "sc-death-log"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "loguru" },
{ name = "pystray" },
{ name = "requests" },
{ name = "sv-ttk" },
]
[package.dev-dependencies]
dev = [
{ name = "nuitka" },
]
[package.metadata]
requires-dist = [{ name = "requests", specifier = ">=2.32.3" }]
requires-dist = [
{ name = "loguru", specifier = ">=0.7.3" },
{ name = "pystray", specifier = ">=0.19.5" },
{ name = "requests", specifier = ">=2.32.3" },
{ name = "sv-ttk", specifier = ">=2.6.0" },
]
[package.metadata.requires-dev]
dev = [{ name = "nuitka", specifier = ">=2.7.3" }]
[[package]]
name = "six"
version = "1.17.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 },
]
[[package]]
name = "sv-ttk"
version = "2.6.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/40/da/6ad667a4bad4d66ec2d15206c1a1ad81d572679e516aae078824a6f35870/sv_ttk-2.6.0.tar.gz", hash = "sha256:3fd440396c95e30e88f686fcf28be425480f7320d6bf346f9cea5d6f56702cc2", size = 47214 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0f/3d/be0abc3202e90f282ad465f4e7c6e41bc8dce810ce5d1611566a1e7dfba8/sv_ttk-2.6.0-py3-none-any.whl", hash = "sha256:4319c52edf2e14732fe84bdc9788e26f9e9a1ad79451ec0f89f0120ffc8105d9", size = 49097 },
]
[[package]]
name = "urllib3"
@ -76,3 +275,39 @@ sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e
wheels = [
{ url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680 },
]
[[package]]
name = "win32-setctime"
version = "1.2.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b3/8f/705086c9d734d3b663af0e9bb3d4de6578d08f46b1b101c2442fd9aecaa2/win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0", size = 4867 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083 },
]
[[package]]
name = "zstandard"
version = "0.23.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cffi", marker = "platform_python_implementation == 'PyPy'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ed/f6/2ac0287b442160a89d726b17a9184a4c615bb5237db763791a7fd16d9df1/zstandard-0.23.0.tar.gz", hash = "sha256:b2d8c62d08e7255f68f7a740bae85b3c9b8e5466baa9cbf7f57f1cde0ac6bc09", size = 681701 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/80/f1/8386f3f7c10261fe85fbc2c012fdb3d4db793b921c9abcc995d8da1b7a80/zstandard-0.23.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:576856e8594e6649aee06ddbfc738fec6a834f7c85bf7cadd1c53d4a58186ef9", size = 788975 },
{ url = "https://files.pythonhosted.org/packages/16/e8/cbf01077550b3e5dc86089035ff8f6fbbb312bc0983757c2d1117ebba242/zstandard-0.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38302b78a850ff82656beaddeb0bb989a0322a8bbb1bf1ab10c17506681d772a", size = 633448 },
{ url = "https://files.pythonhosted.org/packages/06/27/4a1b4c267c29a464a161aeb2589aff212b4db653a1d96bffe3598f3f0d22/zstandard-0.23.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2240ddc86b74966c34554c49d00eaafa8200a18d3a5b6ffbf7da63b11d74ee2", size = 4945269 },
{ url = "https://files.pythonhosted.org/packages/7c/64/d99261cc57afd9ae65b707e38045ed8269fbdae73544fd2e4a4d50d0ed83/zstandard-0.23.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ef230a8fd217a2015bc91b74f6b3b7d6522ba48be29ad4ea0ca3a3775bf7dd5", size = 5306228 },
{ url = "https://files.pythonhosted.org/packages/7a/cf/27b74c6f22541f0263016a0fd6369b1b7818941de639215c84e4e94b2a1c/zstandard-0.23.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:774d45b1fac1461f48698a9d4b5fa19a69d47ece02fa469825b442263f04021f", size = 5336891 },
{ url = "https://files.pythonhosted.org/packages/fa/18/89ac62eac46b69948bf35fcd90d37103f38722968e2981f752d69081ec4d/zstandard-0.23.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f77fa49079891a4aab203d0b1744acc85577ed16d767b52fc089d83faf8d8ed", size = 5436310 },
{ url = "https://files.pythonhosted.org/packages/a8/a8/5ca5328ee568a873f5118d5b5f70d1f36c6387716efe2e369010289a5738/zstandard-0.23.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ac184f87ff521f4840e6ea0b10c0ec90c6b1dcd0bad2f1e4a9a1b4fa177982ea", size = 4859912 },
{ url = "https://files.pythonhosted.org/packages/ea/ca/3781059c95fd0868658b1cf0440edd832b942f84ae60685d0cfdb808bca1/zstandard-0.23.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c363b53e257246a954ebc7c488304b5592b9c53fbe74d03bc1c64dda153fb847", size = 4936946 },
{ url = "https://files.pythonhosted.org/packages/ce/11/41a58986f809532742c2b832c53b74ba0e0a5dae7e8ab4642bf5876f35de/zstandard-0.23.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e7792606d606c8df5277c32ccb58f29b9b8603bf83b48639b7aedf6df4fe8171", size = 5466994 },
{ url = "https://files.pythonhosted.org/packages/83/e3/97d84fe95edd38d7053af05159465d298c8b20cebe9ccb3d26783faa9094/zstandard-0.23.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a0817825b900fcd43ac5d05b8b3079937073d2b1ff9cf89427590718b70dd840", size = 4848681 },
{ url = "https://files.pythonhosted.org/packages/6e/99/cb1e63e931de15c88af26085e3f2d9af9ce53ccafac73b6e48418fd5a6e6/zstandard-0.23.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9da6bc32faac9a293ddfdcb9108d4b20416219461e4ec64dfea8383cac186690", size = 4694239 },
{ url = "https://files.pythonhosted.org/packages/ab/50/b1e703016eebbc6501fc92f34db7b1c68e54e567ef39e6e59cf5fb6f2ec0/zstandard-0.23.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fd7699e8fd9969f455ef2926221e0233f81a2542921471382e77a9e2f2b57f4b", size = 5200149 },
{ url = "https://files.pythonhosted.org/packages/aa/e0/932388630aaba70197c78bdb10cce2c91fae01a7e553b76ce85471aec690/zstandard-0.23.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d477ed829077cd945b01fc3115edd132c47e6540ddcd96ca169facff28173057", size = 5655392 },
{ url = "https://files.pythonhosted.org/packages/02/90/2633473864f67a15526324b007a9f96c96f56d5f32ef2a56cc12f9548723/zstandard-0.23.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa6ce8b52c5987b3e34d5674b0ab529a4602b632ebab0a93b07bfb4dfc8f8a33", size = 5191299 },
{ url = "https://files.pythonhosted.org/packages/b0/4c/315ca5c32da7e2dc3455f3b2caee5c8c2246074a61aac6ec3378a97b7136/zstandard-0.23.0-cp313-cp313-win32.whl", hash = "sha256:a9b07268d0c3ca5c170a385a0ab9fb7fdd9f5fd866be004c4ea39e44edce47dd", size = 430862 },
{ url = "https://files.pythonhosted.org/packages/a2/bf/c6aaba098e2d04781e8f4f7c0ba3c7aa73d00e4c436bcc0cf059a66691d1/zstandard-0.23.0-cp313-cp313-win_amd64.whl", hash = "sha256:f3513916e8c645d0610815c257cbfd3242adfd5c4cfa78be514e5a3ebb42a41b", size = 495578 },
]