Compare commits

..

2 Commits

Author SHA1 Message Date
copilot-swe-agent[bot] 5f5ca84f72 fix: use 100vh instead of fixed 750px for network graph height
Agent-Logs-Url: https://github.com/soxoj/maigret/sessions/943e32b6-d0c4-44a8-a87e-0b77410edbcc

Co-authored-by: soxoj <31013580+soxoj@users.noreply.github.com>
2026-04-30 20:47:20 +00:00
copilot-swe-agent[bot] 98b3dbe7c5 Initial plan 2026-04-30 20:46:36 +00:00
27 changed files with 642 additions and 2593 deletions
+27 -34
View File
@@ -2,48 +2,41 @@ name: Linting and testing
on:
push:
branches: [main]
branches: [ main ]
pull_request:
branches: [main]
branches: [ main ]
types: [opened, synchronize, reopened]
jobs:
build:
runs-on: ubuntu-latest
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libcairo2-dev
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install poetry
python -m poetry install --with dev
- name: Test with Coverage and Pytest (fail if coverage is low)
run: |
poetry run coverage run --source=./maigret -m pytest --reruns 3 --reruns-delay 5 tests
poetry run coverage report --fail-under=60
poetry run coverage html
- name: Upload coverage report
uses: actions/upload-artifact@v4
with:
name: htmlcov-${{ strategy.job-index }}
path: htmlcov
- name: Checkout
uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install system dependencies
run: |
sudo apt-get update && sudo apt-get install -y libcairo2-dev
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install poetry
python -m poetry install --with dev
- name: Test with Coverage and Pytest (Fail if coverage is low)
run: |
poetry run coverage run --source=./maigret -m pytest --reruns 3 --reruns-delay 5 tests
poetry run coverage report --fail-under=60
poetry run coverage html
- name: Upload coverage report
uses: actions/upload-artifact@v4
with:
name: htmlcov-${{ strategy.job-index }}
path: htmlcov
+27 -159
View File
@@ -1,185 +1,53 @@
# How to contribute
Hey! I'm really glad you're reading this. Maigret contains a lot of sites, and it is very hard to keep all the sites operational. That's why any fix is important.
Hey! I'm really glad you're reading this. Maigret contains a lot of sites, and it is very hard to keep all the sites operational. That's why any fix is important.
## Code of Conduct
Please read and follow the [Code of Conduct](CODE_OF_CONDUCT.md) to foster a welcoming and inclusive community.
## Local setup
## How to add a new site
Install Maigret with development dependencies via [Poetry](https://python-poetry.org/):
#### Beginner level
```bash
git clone https://github.com/soxoj/maigret && cd maigret
poetry install --with dev
```
You can use Maigret **submit mode** (`maigret --submit URL`) to add a new site or update an existing site. In this mode Maigret do an automatic analysis of the given account URL or site main page URL to determine the site engine and methods to check account presence. After checking Maigret asks if you want to add the site, answering y/Y will rewrite the local database.
Activate the repo's git hooks **once after cloning**:
#### Advanced level
```bash
git config --local core.hooksPath .githooks/
```
The pre-commit hook does two things every time you commit changes that touch the site database:
- regenerates the database signature `maigret/resources/db_meta.json` (used to detect compatible auto-updates), and
- regenerates `sites.md` (the human-readable list of supported sites with per-engine statistics).
It also auto-stages the regenerated files so they land in the same commit as your edits. **Always run `git commit` from inside the repo so the hook can fire** — without it, your PR will land with a stale signature and a stale `sites.md`, and database auto-update will misbehave for users on your branch.
## How to contribute
There are two main ways to help.
### 1. Add a new site
**Beginner.** Use the `--submit` mode — Maigret takes a single existing-account URL, auto-detects the site engine, picks `presenseStrs` / `absenceStrs`, and offers to add the entry:
```bash
maigret --submit https://example.com/users/alice
```
`--submit` works well when the site has clean status codes and no anti-bot protection. It will *not* discover a public JSON API (`urlProbe`), classify protection (`tls_fingerprint`, `cf_js_challenge`, `ip_reputation`, ...), or recognise SPA / soft-404 pages. For those, fall back to manual editing.
**Advanced.** Edit `maigret/resources/data.json` by hand — see *Editing `data.json` safely* below. There is also an `add-a-site` issue template if you want a maintainer to do it for you.
### 2. Fix existing sites
The most useful work in this project is keeping checks accurate over time. Sites change layout, switch engines, add Cloudflare, redirect to login walls — every fix is welcome.
**Where to start.** Good candidates:
- Issues with the `false-positive` label, especially those opened automatically by the Telegram bot.
- Sites currently `disabled: true` in `data.json` — many were disabled on a transient symptom and have since healed.
- Sites for which `--self-check --diagnose` reports a problem.
- A focused audit of one engine (vBulletin, XenForo, phpBB, Discourse, Flarum, ...). Engine-wide breakage usually has a single root cause and several sites can be fixed in one PR.
**Diagnose with built-in tools.**
> By default, Maigret skips entries with `disabled: true` in every mode (`--self-check`, `--site`, plain search). Whenever your target is a disabled site — diagnosing it, validating a fix, running the two-filter check below — pass **`--use-disabled-sites`** explicitly. Without the flag, the site is silently dropped from the run and you get an empty result that looks like "everything's fine".
- Per-site diagnosis with recommendations:
```bash
maigret --self-check --site "SiteName" --diagnose
# add --use-disabled-sites if the entry is currently disabled
```
Without `--auto-disable`, this only reports — it never edits the database. Add `--auto-disable` only when you really want to write the result back.
- Single-site comparison of claimed vs unclaimed responses (status, markers, headers):
```bash
python utils/site_check.py --site "SiteName" --diagnose
python utils/site_check.py --site "SiteName" --compare-methods # raw aiohttp vs Maigret's checker
```
- Mass check of top-N sites:
```bash
python utils/check_top_n.py --top 100 --only-broken
```
### Understanding `checkType`
Each site entry uses one of three `checkType` modes to decide whether a profile exists. Picking the right one for your site is the most important data-modeling decision in `data.json`:
- **`message`** (most common, most flexible) — Maigret fetches the page and inspects the HTML body. The profile is reported as found when the body contains at least one substring from `presenseStrs` **and** none of the substrings from `absenceStrs`. Pick narrow, profile-specific markers: a `<title>` fragment unique to profile pages, a CSS class only rendered on profiles (e.g. `"profile-card"`), or a JSON field name from an embedded data blob (`"displayName":`). Avoid generic words (`name`, `email`) and HTML/ARIA boilerplate (`polite`, `alert`, `navigation`, `status`) — they match on every page including error and anti-bot challenge pages, and produce false positives. If the marker contains non-ASCII text, double-check the page is UTF-8 (some legacy sites serve KOI8-R or Windows-1251, in which case byte-level matching silently fails — prefer ASCII markers or a JSON API).
- **`status_code`** — Maigret only looks at the HTTP status code; 2xx means "found", anything else means "not found". Use this only when the site reliably returns proper status codes — typically clean JSON APIs that return HTTP 200 for real users and HTTP 404 for missing ones. Don't use it for sites that return HTTP 200 with a soft "user not found" page (this is the single most common cause of false-positive checks).
- **`response_url`** — Maigret follows the redirect chain and inspects the final URL. Useful when the server reliably redirects missing-user URLs to a different path (e.g. `/login`, `/404`, the homepage) while existing-user URLs stay put. For most sites `message` is a better fit; reach for `response_url` only when a redirect-based signal is genuinely the most stable one.
**`urlProbe` (optional, works with any `checkType`).** If the most reliable signal lives at a different URL than the public profile page — a JSON API, a GraphQL endpoint, a mobile-app route — set `urlProbe` to that URL. Maigret fetches `urlProbe` for the check, but reports continue to show the human-readable `url` so users see a profile link they can click. Examples: GitHub uses `https://github.com/{username}` as `url` and `https://api.github.com/users/{username}` as `urlProbe`; Picsart uses the web profile as `url` and `https://api.picsart.com/users/show/{username}.json` as `urlProbe`. A clean public API is almost always more stable than parsing HTML — it's worth probing for one before settling on `message` against the SPA shell.
**Errors vs absence.** Anything that means "the server can't answer right now" — rate limits, captchas, "Checking your browser", "unusual traffic", maintenance pages — belongs in `errors` (mapping the substring to a human-readable error string), not in `absenceStrs`. The `errors` mechanism produces an UNKNOWN result instead of a false CLAIMED or false AVAILABLE.
Full reference for `checkType`, `urlProbe`, `engine`, and the rest of the `data.json` schema is in the [development guide](docs/source/development.rst), section *How to fix false-positives*.
### Editing `data.json` safely
`data.json` is a single ~36 000-line JSON file. **Make surgical, line-level edits only.** Never rewrite it by reading it into a Python dict and dumping it back — `json.load` + `json.dump` reformats every entry and produces an unreviewable 70 000-line diff. The same rule applies to any helper script that touches the file: it must preserve the original formatting of untouched entries.
If your editor reformats JSON on save, disable that for `data.json` before editing.
### Two-filter validation when re-enabling a site
Removing `disabled: true` requires **two** independent checks. `--self-check` alone is not sufficient — it only verifies the two specific usernames recorded in the entry, so a site that returns CLAIMED for *any* arbitrary username will still pass the self-check.
```bash
# Filter 1: self-check on the recorded claimed/unclaimed pair
maigret --self-check --site "SiteName" --use-disabled-sites
# Filter 2: live probe with a clearly fake username — nothing should match
maigret noonewouldeverusethis7 --site "SiteName" --use-disabled-sites --print-not-found
```
Both filters need `--use-disabled-sites`, since a candidate for re-enable still has `disabled: true` in the working tree until your edit lands. If you forget the flag, both commands silently no-op.
If the second command reports `[+]` for the fake username, the check is a false positive — do not enable. This step takes seconds and is non-negotiable for any re-enable PR.
## Site naming, tags, and protection
- **Site naming conventions** (Title Case by default, brand-specific exceptions, no `www.` prefix, etc.) are documented in the [development guide](docs/source/development.rst), section *Site naming conventions*.
- **Country tags** (`us`, `ru`, `kr`, ...) attribute an account to a country of origin or residence — they're not a traffic-share label. Global services (GitHub, YouTube, Reddit) get **no** country tag; regional services (VK → `ru`, Naver → `kr`) **must** have one. Don't assign a country tag from Alexa/SimilarWeb audience stats.
- **Category tags** must come from the canonical `"tags"` array at the bottom of `data.json`. The `test_tags_validity` test fails if you introduce an unregistered tag. If no existing tag fits well, either pick the closest reasonable match or add the new tag to the canonical list as an explicit, separate change. Don't use platform names (`writefreely`, `pixelfed`) — use category names (`blog`, `photo`).
- **Protection tags** (`tls_fingerprint`, `ip_reputation`, `cf_js_challenge`, `cf_firewall`, `aws_waf_js_challenge`, `ddos_guard_challenge`, `js_challenge`, `custom_bot_protection`) describe the kind of anti-bot protection a site uses. One of them — **`tls_fingerprint`** — is load-bearing: when a site fingerprints the TLS handshake (JA3/JA4) and blocks non-browser clients, tagging it with `tls_fingerprint` makes Maigret automatically swap its HTTP client to [`curl_cffi`](https://github.com/lexiforest/curl_cffi) with Chrome browser emulation, which is usually enough to pass. The site stays `enabled` — no `disabled: true` is needed. Examples: Instagram, NPM, Codepen, Kickstarter, Letterboxd. The remaining tags are documentation-only and pair with `disabled: true` until a per-provider solver is integrated. The full taxonomy and the rules for picking the right tag are in the [development guide](docs/source/development.rst), section *protection (site protection tracking)*. Don't add a protection tag without empirical evidence it applies in the current environment.
You can edit [the database JSON file](https://github.com/soxoj/maigret/blob/main/maigret/resources/data.json) (`./maigret/resources/data.json`) manually.
## Testing
CI runs the same checks on every PR, but please run them locally first:
```bash
make format # auto-format with black
make lint # flake / mypy
make test # pytest with coverage
```
There are CI checks for every PR to the Maigret repository. But it will be better to run `make format`, `make link` and `make test` to ensure you've made a corrent changes.
## Submitting changes
Open a [GitHub PR](https://github.com/soxoj/maigret/pulls) against `main`. Always write a clear log message:
To submit you changes you must [send a GitHub PR](https://github.com/soxoj/maigret/pulls) to the Maigret project.
Always write a clear log message for your commits. One-line messages are fine for small changes, but bigger changes should look like this:
```
$ git commit -m "A brief summary of the commit
>
> A paragraph describing what changed and its impact."
```
One-line messages are fine for small changes; bigger changes should explain the *why* in the body.
$ git commit -m "A brief summary of the commit
>
> A paragraph describing what changed and its impact."
## Coding conventions
### General
### General Guidelines
- Follow [PEP 8](https://www.python.org/dev/peps/pep-0008/) for Python.
- Make sure all tests pass before opening the PR.
- Try to follow [PEP 8](https://www.python.org/dev/peps/pep-0008/) for Python code style.
- Ensure your code passes all tests before submitting a pull request.
### Code style
### Code Style
- **Indentation**: 4 spaces per level.
- **Imports**: standard library first, third-party next, project-local last; group them logically.
- **Indentation**: Use 4 spaces per indentation level.
- **Imports**:
- Standard library imports should be placed at the top.
- Third-party imports should follow.
- Group imports logically.
### Naming
### Naming Conventions
- **Variables and functions**: `snake_case`.
- **Classes**: `CamelCase`.
- **Constants**: `UPPER_CASE`.
Start reading the code and you'll get the hang of it.
## Getting help
If you're stuck on something — a check that won't behave, a setup error, an unclear field in `data.json`, or just want to discuss an approach before opening a PR — there are two places to ask:
- [GitHub Discussions](https://github.com/soxoj/maigret/discussions) — searchable, public, good for technical questions and design ideas. Prefer this for anything other contributors might run into too.
- Telegram: [@soxoj](https://t.me/soxoj) — direct channel to the maintainer, good for quick questions and informal chat.
Bug reports and feature requests still belong in [GitHub Issues](https://github.com/soxoj/maigret/issues).
## License
Maigret is MIT-licensed; by submitting a contribution you agree to publish it under the same license. There is no CLA.
- **Variables and Functions**: Use `snake_case`.
- **Classes**: Use `CamelCase`.
- **Constants**: Use `UPPER_CASE`.
Start reading the code and you'll get the hang of it. ;)
-37
View File
@@ -23,10 +23,6 @@
<img src="https://raw.githubusercontent.com/soxoj/maigret/main/static/maigret.png" height="300" alt="Maigret logo"/>
</div>
<br>
<div>
<b>English</b> · <a href="README.zh-CN.md">简体中文</a>
</div>
<br>
</div>
**Maigret** collects a dossier on a person **by username only**, checking for accounts on a huge number of sites and gathering all the available information from web pages. No API keys required.
@@ -69,7 +65,6 @@ See also: [Quick start](https://maigret.readthedocs.io/en/latest/quick-start.htm
- Fetches an [auto-updated site database](https://maigret.readthedocs.io/en/latest/settings.html#database-auto-update) from GitHub each run (once per 24 hours), and falls back to the built-in database if offline.
- Works with Tor and I2P websites; able to check domains.
- Ships with a [web interface](#web-interface) for browsing results as a graph and downloading reports in every format from a single page.
- Optional [AI analysis mode](#ai-analysis) (`--ai`) that turns raw findings into a short investigation summary using an OpenAI-compatible API.
For the complete feature list, see the [features documentation](https://maigret.readthedocs.io/en/latest/features.html).
@@ -196,9 +191,6 @@ maigret user --tags us
# search for three usernames on all available sites
maigret user1 user2 user3 -a
# AI-assisted investigation summary (needs OPENAI_API_KEY)
maigret user --ai
```
Run `maigret --help` for all options. Docs: [CLI options](https://maigret.readthedocs.io/en/latest/command-line-options.html), [more examples](https://maigret.readthedocs.io/en/latest/usage-examples.html). Running into 403s or timeouts? See [TROUBLESHOOTING.md](TROUBLESHOOTING.md).
@@ -234,22 +226,6 @@ See the full [library usage guide](https://maigret.readthedocs.io/en/latest/libr
- `--parse URL` — parse a profile page, extract IDs/usernames, and use them to kick off a recursive search.
- `--permute` — generate likely username variants from two or more inputs (e.g. `john doe``johndoe`, `j.doe`, …) and search for all of them.
- `--self-check [--auto-disable]` — verify `usernameClaimed` / `usernameUnclaimed` pairs against live sites for maintainers auditing the database.
- `--ai` / `--ai-model` — run the [AI analysis](#ai-analysis) over the search results and stream a short investigation summary to the terminal.
<a id="ai-analysis"></a>
### AI analysis
`--ai` collects the search results, builds an internal Markdown report, and sends it to an OpenAI-compatible chat completion endpoint to produce a short, neutral investigation summary (likely real name, location, occupation, interests, languages, confidence, follow-up leads). Per-site progress is suppressed and the model's output is streamed to stdout.
```bash
export OPENAI_API_KEY=sk-...
maigret user --ai
# pick a different model
maigret user --ai --ai-model gpt-4o-mini
```
The key can also be set as `openai_api_key` in `settings.json`. The endpoint defaults to `https://api.openai.com/v1`, but `openai_api_base_url` in `settings.json` can point to any OpenAI-compatible API (Azure OpenAI, OpenRouter, a local server, …). See the [settings docs](https://maigret.readthedocs.io/en/latest/settings.html) for the full list of options.
### Tor / I2P / proxies
@@ -268,19 +244,6 @@ maigret user --i2p-proxy http://127.0.0.1:4444
Start your Tor / I2P daemon before running the command — Maigret does not manage these gateways.
### Cloudflare bypass
> **Experimental.** The Cloudflare webgate is under active development; the configuration schema, CLI behaviour, and the set of routed sites may change without backwards-compatibility guarantees.
A subset of sites in the database require a real browser to solve a JavaScript challenge. Maigret can offload these checks to a local [FlareSolverr](https://github.com/FlareSolverr/FlareSolverr) instance:
```bash
docker run -d -p 8191:8191 --name flaresolverr ghcr.io/flaresolverr/flaresolverr:latest
maigret --cloudflare-bypass <username>
```
The bypass is opt-in (`--cloudflare-bypass` or `cloudflare_bypass.enabled` in `settings.json`) and only fires for sites whose `protection` field matches. See the [feature docs](https://maigret.readthedocs.io/en/latest/features.html#cloudflare-bypass) for backend options and configuration.
## Contributing
Add or fix new sites surgically in `data.json` (no `json.load`/`json.dump`), then run `./utils/update_site_data.py` to regenerate `sites.md` and the database metadata, and open a pull request. For more details, see the [CONTRIBUTING guide](https://github.com/soxoj/maigret/blob/main/CONTRIBUTING.md) and [development docs](https://maigret.readthedocs.io/en/latest/development.html). Release history: [CHANGELOG.md](CHANGELOG.md).
-310
View File
@@ -1,310 +0,0 @@
# Maigret
<div align="center">
<div>
<a href="https://pypi.org/project/maigret/">
<img alt="Maigret 的 PyPI 版本" src="https://img.shields.io/pypi/v/maigret?style=flat-square" />
</a>
<a href="https://pypi.org/project/maigret/">
<img alt="Maigret 的 PyPI 周下载量" src="https://img.shields.io/pypi/dw/maigret?style=flat-square" />
</a>
<a href="https://github.com/soxoj/maigret">
<img alt="所需最低 Python 版本:3.10+" src="https://img.shields.io/badge/Python-3.10%2B-brightgreen?style=flat-square" />
</a>
<a href="https://github.com/soxoj/maigret/blob/main/LICENSE">
<img alt="Maigret 的开源许可证" src="https://img.shields.io/github/license/soxoj/maigret?style=flat-square" />
</a>
<a href="https://github.com/soxoj/maigret">
<img alt="Maigret 项目访问量" src="https://komarev.com/ghpvc/?username=maigret&color=brightgreen&label=views&style=flat-square" />
</a>
</div>
<br>
<div>
<img src="https://raw.githubusercontent.com/soxoj/maigret/main/static/maigret.png" height="300" alt="Maigret logo"/>
</div>
<br>
<div>
<a href="README.md">English</a> · <b>简体中文</b>
</div>
<br>
</div>
**Maigret** 仅凭一个用户名,就能在大量站点上查找其账号,并从网页中收集所有可获取的公开信息,为目标人物生成一份档案。无需任何 API 密钥。
## 目录
- [一分钟上手](#one-minute)
- [核心特性](#main-features)
- [演示](#demo)
- [安装](#installation)
- [使用](#usage)
- [参与贡献](#contributing)
- [商业使用](#commercial-use)
- [关于](#about)
<a id="one-minute"></a>
## 一分钟上手
请先确认本机的 Python 版本不低于 3.10。
```bash
pip install maigret
maigret YOUR_USERNAME
```
不想本地安装?可以试试 [Telegram 机器人](https://t.me/maigret_search_bot),或者使用[云端 Shell](#cloud-shells)。
想要一个 Web 界面?参见[启动方式](#web-interface)。
延伸阅读:[快速入门](https://maigret.readthedocs.io/en/latest/quick-start.html)。
<a id="main-features"></a>
## 核心特性
- 支持 3000+ 站点(完整列表见 [sites.md](https://github.com/soxoj/maigret/blob/main/sites.md))。默认仅检查访问量排名前 500 的站点;加上 `-a` 可全量扫描,或使用 `--tags` 按分类/国家筛选。
- 可作为 Python 库嵌入到自己的项目中——直接 `import maigret` 即可在代码里发起搜索(参见[库使用文档](https://maigret.readthedocs.io/en/latest/library-usage.html))。
- 通过 [socid_extractor](https://github.com/soxoj/socid_extractor) 从个人主页和站点 API 中[提取](https://github.com/soxoj/socid_extractor)账号所有者的所有可获取信息,包括指向其他账号的链接。
- 基于已发现的用户名和其他 ID,执行递归搜索。
- 支持按标签(站点分类、国家)进行筛选。
- 能够检测并部分绕过封锁、审查和 CAPTCHA。
- 每次运行时(每 24 小时一次)从 GitHub 拉取一份[自动更新的站点数据库](https://maigret.readthedocs.io/en/latest/settings.html#database-auto-update);离线时会回退到内置数据库。
- 可访问 Tor 与 I2P 站点;支持检查域名。
- 自带一个 [Web 界面](#web-interface),可在同一页面将结果以图谱方式浏览,并下载各种格式的报告。
- 可选的 [AI 分析模式](#ai-analysis)(`--ai`),通过 OpenAI 兼容 API 将原始搜索结果整理成一份简短的调查摘要。
完整特性列表请见[特性文档](https://maigret.readthedocs.io/en/latest/features.html)。
### 谁在使用
基于 Maigret 构建的专业 OSINT 与社交媒体分析工具:
<a href="https://github.com/SocialLinks-IO/sociallinks-api"><img height="60" alt="Social Links API" src="https://github.com/user-attachments/assets/789747b2-d7a0-4d4e-8868-ffc4427df660"></a>
<a href="https://sociallinks.io/products/sl-crimewall"><img height="60" alt="Social Links Crimewall" src="https://github.com/user-attachments/assets/0b18f06c-2f38-477b-b946-1be1a632a9d1"></a>
<a href="https://usersearch.ai/"><img height="60" alt="UserSearch" src="https://github.com/user-attachments/assets/66daa213-cf7d-40cf-9267-42f97cf77580"></a>
<a id="demo"></a>
## 演示
### 视频
<a href="https://asciinema.org/a/Ao0y7N0TTxpS0pisoprQJdylZ">
<img src="https://asciinema.org/a/Ao0y7N0TTxpS0pisoprQJdylZ.svg" alt="asciicast" width="600">
</a>
### 报告示例
[PDF 报告](https://raw.githubusercontent.com/soxoj/maigret/main/static/report_alexaimephotographycars.pdf)、[HTML 报告](https://htmlpreview.github.io/?https://raw.githubusercontent.com/soxoj/maigret/main/static/report_alexaimephotographycars.html)
![HTML 报告截图](https://raw.githubusercontent.com/soxoj/maigret/main/static/report_alexaimephotography_html_screenshot.png)
![XMind 8 报告截图](https://raw.githubusercontent.com/soxoj/maigret/main/static/report_alexaimephotography_xmind_screenshot.png)
[完整的命令行输出示例](https://raw.githubusercontent.com/soxoj/maigret/main/static/recursive_search.md)
<a id="installation"></a>
## 安装
如果你已经按[一分钟上手](#one-minute)的步骤跑通了,就无需再装。下面列出几种可选的安装方式。
什么都不想装?直接用 [Telegram 机器人](https://t.me/maigret_search_bot)。
### Windows
从 [Releases](https://github.com/soxoj/maigret/releases) 下载独立的 EXE 文件。视频指引:https://youtu.be/qIgwTZOmMmM。
<a id="cloud-shells"></a>
### 云端 Shell
通过云端 Shell 或 Jupyter Notebook 在浏览器里运行 Maigret:
<a href="https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/soxoj/maigret&tutorial=cloudshell-tutorial.md"><img src="https://user-images.githubusercontent.com/27065646/92304704-8d146d80-ef80-11ea-8c29-0deaabb1c702.png" alt="Open in Cloud Shell" height="50"></a>
<a href="https://repl.it/github/soxoj/maigret"><img src="https://replit.com/badge/github/soxoj/maigret" alt="Run on Replit" height="50"></a>
<a href="https://colab.research.google.com/gist/soxoj/879b51bc3b2f8b695abb054090645000/maigret-collab.ipynb"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab" height="45"></a>
<a href="https://mybinder.org/v2/gist/soxoj/9d65c2f4d3bec5dd25949197ea73cf3a/HEAD"><img src="https://mybinder.org/badge_logo.svg" alt="Open In Binder" height="45"></a>
### 本地安装(pip)
```bash
# 从 PyPI 安装
pip3 install maigret
# 使用
maigret username
```
### 从源码安装
```bash
# 也可以克隆仓库后手动安装
git clone https://github.com/soxoj/maigret && cd maigret
# 构建并安装
pip3 install .
# 使用
maigret username
```
### Docker
官方提供两个镜像变体:
- `soxoj/maigret:latest` —— CLI 模式(默认)
- `soxoj/maigret:web` —— 自动启动 [Web 界面](#web-interface)
```bash
# 拉取官方镜像(CLI)
docker pull soxoj/maigret
# CLI 用法
docker run -v /mydir:/app/reports soxoj/maigret:latest username --html
# Web UI(在 http://localhost:5000 打开)
docker run -p 5000:5000 soxoj/maigret:web
# 自定义 Web UI 端口
docker run -e PORT=8080 -p 8080:8080 soxoj/maigret:web
# 手动构建
docker build -t maigret . # CLI 镜像(默认 target)
docker build --target web -t maigret-web . # Web UI 镜像
```
### 故障排查
构建报错?请见[故障排查指南](https://maigret.readthedocs.io/en/latest/installation.html#troubleshooting)。
<a id="usage"></a>
## 使用
### 示例
```bash
# 生成 HTML、PDF、XMind 8 报告
maigret user --html
maigret user --pdf
maigret user --xmind # 与 XMind 2022+ 不兼容
# 机器可读的导出格式
maigret user --json ndjson # 行分隔 JSON(也支持 --json simple)
maigret user --csv
maigret user --txt
maigret user --graph # 交互式 D3 图谱(HTML)
# 仅在带有 photo 与 dating 标签的站点上搜索
maigret user --tags photo,dating
# 仅在带有 us 标签的站点上搜索
maigret user --tags us
# 同时在所有站点上搜索三个用户名
maigret user1 user2 user3 -a
# AI 辅助调查摘要(需要 OPENAI_API_KEY)
maigret user --ai
```
完整选项请运行 `maigret --help`。文档:[命令行选项](https://maigret.readthedocs.io/en/latest/command-line-options.html)、[更多示例](https://maigret.readthedocs.io/en/latest/usage-examples.html)。遇到 403 或超时?参见 [TROUBLESHOOTING.md](TROUBLESHOOTING.md)。
<a id="web-interface"></a>
### Web 界面
Maigret 内置一个 Web UI,提供结果图谱视图和报告下载。
<details>
<summary>Web 界面截图</summary>
![Web 界面:启动页](https://raw.githubusercontent.com/soxoj/maigret/main/static/web_interface_screenshot_start.png)
![Web 界面:结果页](https://raw.githubusercontent.com/soxoj/maigret/main/static/web_interface_screenshot.png)
</details>
```console
maigret --web 5000
```
在浏览器中打开 http://127.0.0.1:5000,输入用户名即可查看结果。
### Python 库
**Maigret 可以嵌入到你自己的 Python 项目里使用。** CLI 只是对一个异步函数的薄包装,你完全可以直接调用它——构建自定义流水线、把结果接入自家工具,或将其嵌入更大的 OSINT 工作流。
完整示例(包含异步用法和按标签筛选站点)请参见[库使用指南](https://maigret.readthedocs.io/en/latest/library-usage.html)。
### 常用 CLI 参数
- `--parse URL` —— 解析一个个人主页,从中提取 ID/用户名,并以此为起点发起递归搜索。
- `--permute` —— 基于两个或更多输入生成可能的用户名变体(例如 `john doe``johndoe``j.doe` …)并对其逐一搜索。
- `--self-check [--auto-disable]` —— 维护者用于核对数据库的工具:针对线上站点验证 `usernameClaimed` / `usernameUnclaimed` 配对是否仍然有效。
- `--ai` / `--ai-model` —— 启用 [AI 分析](#ai-analysis),将搜索结果交给 OpenAI 兼容 API,并把简短的调查摘要流式输出到终端。
<a id="ai-analysis"></a>
### AI 分析
`--ai` 会先收集搜索结果、在内存中构建 Markdown 报告,再将其发送到一个 OpenAI 兼容的 chat completion 接口,生成一份简短、克制的调查摘要(最可能的真实姓名、所在地、职业、兴趣、语言、置信度以及后续线索)。开启该模式后,逐站点的进度输出会被静默,模型的输出会以流式方式打印到 stdout。
```bash
export OPENAI_API_KEY=sk-...
maigret user --ai
# 切换到其它模型
maigret user --ai --ai-model gpt-4o-mini
```
API key 也可以写入 `settings.json``openai_api_key` 字段。接口地址默认为 `https://api.openai.com/v1`,通过在 `settings.json` 中设置 `openai_api_base_url`,可以指向任何 OpenAI 兼容的服务(Azure OpenAI、OpenRouter、本地推理服务等)。完整选项见[配置文档](https://maigret.readthedocs.io/en/latest/settings.html)。
### Tor / I2P / 代理
Maigret 支持通过代理、Tor 或 I2P 转发请求——这对访问 `.onion` / `.i2p` 站点,以及绕过会拦截数据中心 IP 的 WAF 都很有用。
```bash
# 任意 HTTP/SOCKS 代理
maigret user --proxy socks5://127.0.0.1:1080
# Tor(默认网关 socks5://127.0.0.1:9050)
maigret user --tor-proxy socks5://127.0.0.1:9050
# I2P(默认网关 http://127.0.0.1:4444)
maigret user --i2p-proxy http://127.0.0.1:4444
```
请先启动 Tor / I2P 守护进程再运行上述命令——Maigret 不会替你管理这些网关。
<a id="contributing"></a>
## 参与贡献
请精确地在 `data.json` 里新增或修复站点(不要使用 `json.load`/`json.dump` 整体读写),然后运行 `./utils/update_site_data.py` 重新生成 `sites.md` 和数据库元数据,再提交 Pull Request。更多细节见 [CONTRIBUTING 指南](https://github.com/soxoj/maigret/blob/main/CONTRIBUTING.md) 和[开发文档](https://maigret.readthedocs.io/en/latest/development.html)。版本历史见 [CHANGELOG.md](CHANGELOG.md)。
<a id="commercial-use"></a>
## 商业使用
开源版本的 Maigret 采用 MIT 许可证,可不受限制地用于商业用途——但站点检查会随时间失效,需要持续维护。
如果你有更严肃的商业需求——希望使用**每日更新的站点数据库**或**用户名查询 API**——欢迎联系:📧 [maigret@soxoj.com](mailto:maigret@soxoj.com)
- 私有站点数据库 —— 5000+ 站点,每日更新(独立于公开开源数据库)
- 用户名查询 API —— 将 Maigret 集成进你的产品
<a id="about"></a>
## 关于
### 免责声明
**仅供教育与合法用途。** 使用者需自行承担遵守所在司法辖区相关法律(GDPR、CCPA 等)的责任。作者不对任何滥用行为负责。
### 反馈
[提交 issue](https://github.com/soxoj/maigret/issues) · [GitHub Discussions](https://github.com/soxoj/maigret/discussions) · [Telegram](https://t.me/soxoj)
### SOWEL 分类
涉及到的 OSINT 技术:
- [SOTL-2.2. Search For Accounts On Other Platforms](https://sowel.soxoj.com/other-platform-accounts)
- [SOTL-6.1. Check Logins Reuse To Find Another Account](https://sowel.soxoj.com/logins-reuse)
- [SOTL-6.2. Check Nicknames Reuse To Find Another Account](https://sowel.soxoj.com/nicknames-reuse)
### 许可证
MIT © [Maigret](https://github.com/soxoj/maigret)
-67
View File
@@ -95,17 +95,6 @@ the run after the explicit update finishes.
``--retries RETRIES`` - Count of attempts to restart temporarily failed
requests.
``--cloudflare-bypass`` *(experimental)* - Route checks for sites tagged
``protection: ["cf_js_challenge"]`` / ``["cf_firewall"]`` / ``["webgate"]``
through a local Chrome-based solver (FlareSolverr by default). The bypass
is opt-in — without this flag (or
``settings.cloudflare_bypass.enabled = true``) those sites are checked
the usual way, which Cloudflare almost always blocks: you get an UNKNOWN
status with a JS-challenge / firewall error rather than a real result.
Configure the backend in ``settings.cloudflare_bypass.modules``.
See :ref:`cloudflare-bypass`. **Experimental** — the flag, schema and
routing rules may change without backwards-compatibility guarantees.
.. _custom-database:
Using a custom sites database
@@ -172,14 +161,6 @@ ndjson (one report per username). E.g. ``--json ndjson``
``-M``, ``--md`` - Generate a Markdown report (general report on all
usernames). See :ref:`markdown-report` below.
``--ai`` - Run an AI-powered analysis of the search results using an
OpenAI-compatible chat completion API. The internal Markdown report is
sent to the model, which returns a short investigation summary that is
streamed to the terminal. See :ref:`ai-analysis` below.
``--ai-model`` - Model name to use with ``--ai``. Defaults to
``openai_model`` from settings (``gpt-4o`` out of the box).
``-fo``, ``--folderoutput`` - Results will be saved to this folder,
``results`` by default. Will be created if doesnt exist.
@@ -261,51 +242,3 @@ The Markdown format is optimized for LLM context windows. You can feed the repor
The structured Markdown with per-site sections makes it easy for AI tools to extract relationships, cross-reference identities, and identify patterns across accounts.
For a built-in alternative that calls the model for you and prints the
summary directly, see :ref:`ai-analysis` below.
.. _ai-analysis:
AI analysis (built-in)
----------------------
The ``--ai`` flag turns the search results into a short investigation
summary by sending the internal Markdown report to an OpenAI-compatible
chat completion API and streaming the model's reply to the terminal.
.. code-block:: console
export OPENAI_API_KEY=sk-...
maigret username --ai
# use a smaller / cheaper model
maigret username --ai --ai-model gpt-4o-mini
While ``--ai`` is active, per-site progress lines and the short text
report at the end are suppressed so the streamed summary is the main
output. The Markdown report itself is built in memory and is **not**
written to disk by ``--ai`` alone — combine with ``--md`` if you also
want the file on disk.
The summary follows a fixed format with sections for the most likely
real name, location, occupation, interests, languages, main website,
username variants, number of platforms, active years, a confidence
rating, and a short list of follow-up leads. The model is instructed
to rely only on what is supported by the report and to avoid mixing
clearly unrelated profiles into the main identity.
**Configuration.** The API key is resolved from
``settings.openai_api_key`` first, then from the ``OPENAI_API_KEY``
environment variable. The endpoint defaults to
``https://api.openai.com/v1`` and can be redirected to any
OpenAI-compatible service (Azure OpenAI, OpenRouter, a local server,
…) by setting ``openai_api_base_url`` in ``settings.json``. See
:ref:`settings` for the full list of options.
.. note::
``--ai`` makes a network request to the configured chat completion
endpoint and sends the full Markdown report (which contains the
gathered profile data). Use it only with providers and accounts
you trust with that data.
+1 -1
View File
@@ -338,7 +338,7 @@ Documentations is auto-generated and auto-deployed from the ``docs`` directory.
To manually update documentation:
1. Change something in the ``.rst`` files in the ``docs/source`` directory.
2. Install ``python -m pip install -e .`` in the docs directory.
2. Install ``pip install -r requirements.txt`` in the docs directory.
3. Run ``make singlehtml`` in the terminal in the docs directory.
4. Open ``build/singlehtml/index.html`` in your browser to see the result.
5. If everything is ok, commit and push your changes to GitHub.
-82
View File
@@ -147,33 +147,6 @@ Also, there is a short text report in the CLI output after the end of a searchin
.. warning::
XMind 8 mindmaps are incompatible with XMind 2022!
AI analysis
-----------
Maigret can produce a short, human-readable investigation summary on top
of the raw search results using the ``--ai`` flag. It builds the
internal Markdown report, sends it to an OpenAI-compatible chat
completion endpoint, and streams the model's reply directly to the
terminal.
.. code-block:: console
export OPENAI_API_KEY=sk-...
maigret username --ai
The summary uses a fixed format with the most likely real name,
location, occupation, interests, languages, main website, username
variants, number of platforms, active years, a confidence rating, and a
short list of follow-up leads. While ``--ai`` is active, per-site
progress and the short text report are suppressed so the streamed
summary is the main output.
The endpoint, model, and API key are configured via ``settings.json``
(``openai_api_key``, ``openai_model``, ``openai_api_base_url``) or the
``OPENAI_API_KEY`` environment variable. Any OpenAI-compatible API can
be used (Azure OpenAI, OpenRouter, a local server, …). See
:ref:`ai-analysis` and :ref:`settings` for details.
Tags
----
@@ -237,61 +210,6 @@ The Maigret database contains not only the original websites, but also mirrors,
It allows getting additional info about the person and checking the existence of the account even if the main site is unavailable (bot protection, captcha, etc.)
.. _cloudflare-bypass:
Cloudflare webgate bypass
-------------------------
.. warning::
**Experimental feature.** The Cloudflare webgate is under active
development. The configuration schema, CLI flag behaviour, and the set
of sites that route through it may change without backwards-compatibility
guarantees. Expect rough edges (CF rate limits, occasional solver
failures) and report issues so they can be ironed out.
Some sites sit behind a full Cloudflare JavaScript challenge or a CF firewall
hard block — these are tagged ``protection: ["cf_js_challenge"]`` or
``protection: ["cf_firewall"]`` in the database and are normally kept disabled
because neither aiohttp nor curl_cffi can solve the JS challenge on their own.
Maigret can offload these checks to a local Chrome-based solver. Two backends
are supported, configured in ``settings.json`` under
``cloudflare_bypass.modules`` (the first reachable module wins; subsequent
ones are tried as a fallback chain):
* **FlareSolverr** (recommended). Runs a real Chrome instance and exposes a
JSON API. The upstream HTTP status, headers and final URL are preserved, so
``checkType: status_code`` and ``checkType: response_url`` keep working
through the bypass.
.. code-block:: console
docker run -d -p 8191:8191 --name flaresolverr ghcr.io/flaresolverr/flaresolverr:latest
* **CloudflareBypassForScraping** (legacy fallback). Returns rendered HTML
only, so the upstream status code is lost — ``checkType: message`` keeps
working but ``status_code`` checks misfire (treated as 200 on success).
Activate the bypass either with the CLI flag::
maigret --cloudflare-bypass <username>
or by setting ``cloudflare_bypass.enabled`` to ``true`` in ``settings.json``.
The bypass only fires for sites whose ``protection`` field intersects
``cloudflare_bypass.trigger_protection`` (default
``["cf_js_challenge", "cf_firewall", "webgate"]``); all other sites use the
normal aiohttp / curl_cffi path.
If all configured modules are unreachable, affected sites get an UNKNOWN
status with an actionable error pointing at the first module's URL — the
fix is almost always to start the FlareSolverr container.
FlareSolverr session reuse is automatic: Maigret pins a single
``session: <session_prefix>-<pid>`` per run, so cf_clearance cookies are
shared between checks of the same domain (510× faster on subsequent
requests to that host).
Activation
----------
The activation mechanism helps make requests to sites requiring additional authentication like cookies, JWT tokens, or custom headers.
-22
View File
@@ -125,25 +125,3 @@ After installing the system dependencies, retry the maigret installation.
If you continue to have issues, consider using Docker instead, which includes all
necessary dependencies.
Optional: Cloudflare bypass solver
----------------------------------
.. warning::
**Experimental.** The Cloudflare webgate is under active development;
the configuration schema and CLI behaviour may change without
backwards-compatibility guarantees.
Sites tagged ``cf_js_challenge`` / ``cf_firewall`` need a real browser to pass
their JavaScript challenge. To check those sites you can run a local
`FlareSolverr <https://github.com/FlareSolverr/FlareSolverr>`_ instance —
Maigret will route protected checks to it when ``--cloudflare-bypass`` is set:
.. code-block:: bash
docker run -d -p 8191:8191 --name flaresolverr ghcr.io/flaresolverr/flaresolverr:latest
This is **optional** — Maigret runs without it; only sites whose
``protection`` field intersects ``settings.cloudflare_bypass.trigger_protection``
require the solver. See :ref:`cloudflare-bypass` for details.
-137
View File
@@ -101,140 +101,3 @@ This is recommended for **Docker containers**, **CI pipelines**, and **air-gappe
- URL of the metadata file (for custom mirrors)
**Using a custom database** with ``--db`` always skips auto-update — you are explicitly choosing your data source.
Cloudflare webgate
------------------
.. warning::
**Experimental.** The ``cloudflare_bypass`` block is under active
development; field names, defaults, and the trigger-protection routing
rules may change without backwards-compatibility guarantees.
The ``cloudflare_bypass`` block in ``settings.json`` configures the optional
bypass described in :ref:`cloudflare-bypass`. Default value:
.. code-block:: json
{
"cloudflare_bypass": {
"enabled": false,
"session_prefix": "maigret",
"trigger_protection": ["cf_js_challenge", "cf_firewall", "webgate"],
"modules": [
{
"name": "flaresolverr",
"method": "json_api",
"url": "http://localhost:8191/v1",
"max_timeout_ms": 60000
},
{
"name": "chrome_webgate",
"method": "url_rewrite",
"url": "http://localhost:8000/html?url={url}&retries=1"
}
]
}
}
**Fields.**
.. list-table::
:header-rows: 1
:widths: 30 70
* - Field
- Description
* - ``enabled``
- When ``true``, the bypass is active for every run; when ``false``
(the default), it activates only on ``--cloudflare-bypass``.
* - ``trigger_protection``
- List of ``site.protection`` values that route a check through the
webgate. Sites whose protection is empty or doesn't intersect this
list use the default (aiohttp / curl_cffi) checker.
* - ``session_prefix``
- Prefix for the FlareSolverr ``session`` field. Maigret appends the
process PID so concurrent runs don't collide. Reusing a session
caches cf_clearance between checks of the same domain.
* - ``modules``
- Ordered list of backend modules. The first reachable module
handles the check; later ones serve as a fallback chain.
**Module methods.**
* ``json_api`` — FlareSolverr-compatible POST endpoint at ``url``.
Preserves real upstream HTTP status, headers and final URL.
Optional ``max_timeout_ms`` (default ``60000``) is the per-request
budget the solver is allowed to spend on the JS challenge.
* ``url_rewrite`` — legacy CloudflareBypassForScraping endpoint. The
``url`` must contain a ``{url}`` placeholder; the original probe URL
is URL-encoded and substituted in. Returns rendered HTML only —
``checkType: status_code`` and ``response_url`` checks misfire under
this method (treated as a synthetic HTTP 200 on success).
**Optional ``proxy`` field (``json_api`` only).**
A module may carry a ``proxy`` entry that the solver routes the upstream
request through. Useful when a site enforces ``ip_reputation`` rules
that block the solver host. Two forms are accepted:
.. code-block:: json
{ "proxy": "socks5://localhost:1080" }
.. code-block:: json
{ "proxy": { "url": "http://gw.example:3128",
"username": "u",
"password": "p" } }
Only ``url``/``username``/``password`` are forwarded; other keys are
dropped. Cloudflare ``Error 1015 / 1020`` responses indicate the IP is
rate-limited or banned — switch the proxy rather than retrying.
.. _ai-analysis-settings:
AI analysis
-----------
The ``--ai`` flag (see :ref:`ai-analysis`) talks to an OpenAI-compatible
chat completion API. Three settings control how that request is made:
.. list-table::
:header-rows: 1
:widths: 35 25 40
* - Setting
- Default
- Description
* - ``openai_api_key``
- ``""`` (empty)
- API key. If empty, Maigret falls back to the ``OPENAI_API_KEY``
environment variable.
* - ``openai_model``
- ``gpt-4o``
- Default model name. Overridable per-run with ``--ai-model``.
* - ``openai_api_base_url``
- ``https://api.openai.com/v1``
- Base URL of the chat completion API. Point this at any
OpenAI-compatible service (Azure OpenAI, OpenRouter, a local
server, …) to use it instead of OpenAI directly.
Example ``~/.maigret/settings.json`` snippet using a non-OpenAI
endpoint:
.. code-block:: json
{
"openai_api_key": "sk-...",
"openai_model": "gpt-4o-mini",
"openai_api_base_url": "https://openrouter.ai/api/v1"
}
The key resolution order is ``settings.openai_api_key````OPENAI_API_KEY``
environment variable; the first non-empty value wins.
.. note::
``--ai`` sends the full internal Markdown report (which contains the
gathered profile data) to the configured endpoint. Only use providers
and accounts you trust with that data.
+1 -12
View File
@@ -7,18 +7,7 @@ __author_email__ = 'soxoj@protonmail.com'
from .__version__ import __version__
try:
from .checking import maigret as search
except ImportError as e:
raise ImportError(
"Missing required dependency while starting Maigret.\n\n"
"If installed from PyPI:\n"
" pip install -U maigret\n\n"
"If running from a cloned repository:\n"
" pip install -e .\n\n"
"Then run Maigret as:\n"
" python -m maigret <username>"
) from e
from .checking import maigret as search
from .maigret import main as cli
from .sites import MaigretEngine, MaigretSite, MaigretDatabase
from .notify import QueryNotifyPrint as Notifier
+38 -42
View File
@@ -75,45 +75,6 @@ async def print_streaming(text: str, delay: float = 0.04):
sys.stdout.flush()
async def _check_response(resp):
"""Raise descriptive errors for non-success HTTP responses."""
if resp.status == 401:
raise RuntimeError("Invalid OpenAI API key (HTTP 401)")
if resp.status == 429:
raise RuntimeError("OpenAI API rate limit exceeded (HTTP 429)")
if resp.status != 200:
body = await resp.text()
raise RuntimeError(f"OpenAI API error (HTTP {resp.status}): {body[:500]}")
async def _stream_response(resp, spinner, first_token):
"""Stream tokens from resp, display them, and return (first_token, full_analysis)."""
full_response = []
async for line in resp.content:
decoded = line.decode("utf-8").strip()
if not decoded or not decoded.startswith("data: "):
continue
data_str = decoded[len("data: "):]
if data_str == "[DONE]":
break
try:
chunk = json.loads(data_str)
except json.JSONDecodeError:
continue
delta = chunk.get("choices", [{}])[0].get("delta", {})
content = delta.get("content", "")
if not content:
continue
if first_token:
spinner.stop()
print()
first_token = False
sys.stdout.write(content)
sys.stdout.flush()
full_response.append(content)
return first_token, "".join(full_response)
async def get_ai_analysis(
api_key: str,
markdown_report: str,
@@ -144,12 +105,47 @@ async def get_ai_analysis(
spinner = _Spinner("Analysing the data with AI...")
spinner.start()
first_token = True
full_response = []
try:
async with aiohttp.ClientSession() as session:
async with session.post(url, json=payload, headers=headers) as resp:
await _check_response(resp)
first_token, analysis = await _stream_response(resp, spinner, first_token)
if resp.status == 401:
raise RuntimeError("Invalid OpenAI API key (HTTP 401)")
if resp.status == 429:
raise RuntimeError("OpenAI API rate limit exceeded (HTTP 429)")
if resp.status != 200:
body = await resp.text()
raise RuntimeError(
f"OpenAI API error (HTTP {resp.status}): {body[:500]}"
)
async for line in resp.content:
decoded = line.decode("utf-8").strip()
if not decoded or not decoded.startswith("data: "):
continue
data_str = decoded[len("data: "):]
if data_str == "[DONE]":
break
try:
chunk = json.loads(data_str)
except json.JSONDecodeError:
continue
delta = chunk.get("choices", [{}])[0].get("delta", {})
content = delta.get("content", "")
if not content:
continue
if first_token:
spinner.stop()
print()
first_token = False
sys.stdout.write(content)
sys.stdout.flush()
except Exception:
spinner.stop()
raise
@@ -159,4 +155,4 @@ async def get_ai_analysis(
spinner.stop()
print()
return analysis
return "".join(full_response)
+4 -288
View File
@@ -2,7 +2,6 @@
import ast
import asyncio
import logging
import os
import random
import re
import ssl
@@ -49,53 +48,6 @@ SUPPORTED_IDS = (
BAD_CHARS = "#"
def build_cloudflare_bypass_config(
settings_obj: Optional[Any], force_enable: bool = False
) -> Optional[Dict[str, Any]]:
"""Resolve Cloudflare webgate config from settings + CLI flag.
Returns ``None`` when bypass is inactive or no usable module is configured.
Otherwise returns a dict consumed by ``CloudflareWebgateChecker``:
- ``trigger_protection``: list of ``site.protection`` values that
activate the bypass (e.g. ``["cf_js_challenge", "cf_firewall", "webgate"]``)
- ``modules``: ordered list of backend modules to try; each entry has
``name``, ``method`` (``json_api`` for FlareSolverr, ``url_rewrite``
for CloudflareBypassForScraping), and a method-specific ``url`` plus
optional ``max_timeout_ms``.
- ``session_prefix``: prefix for FlareSolverr session reuse.
"""
raw = {}
if settings_obj is not None:
raw = getattr(settings_obj, "cloudflare_bypass", {}) or {}
enabled = bool(force_enable) or bool(raw.get("enabled", False))
if not enabled:
return None
modules_raw = raw.get("modules") or []
valid_modules: List[Dict[str, Any]] = []
for module in modules_raw:
method = module.get("method")
url = module.get("url")
if method == "json_api" and url:
valid_modules.append(dict(module))
elif method == "url_rewrite" and url and "{url}" in url:
valid_modules.append(dict(module))
if not valid_modules:
return None
trigger = raw.get("trigger_protection") or [
"cf_js_challenge",
"cf_firewall",
"webgate",
]
return {
"trigger_protection": list(trigger),
"modules": valid_modules,
"session_prefix": raw.get("session_prefix", "maigret"),
}
class CheckerBase:
pass
@@ -335,221 +287,6 @@ class CurlCffiChecker(CheckerBase):
return None, 0, CheckError("Unexpected", str(e))
class CloudflareWebgateChecker(CheckerBase):
"""Sends checks through a Cloudflare-bypass proxy.
Supports two backends, selected by ``modules[0].method`` in settings:
- ``json_api`` (FlareSolverr): POST to ``/v1`` with ``cmd: request.get``.
Preserves real upstream status_code, headers and final URL — drop-in
replacement for SimpleAiohttpChecker.
- ``url_rewrite`` (CloudflareBypassForScraping ``/html`` endpoint):
legacy mode. Returns rendered HTML only. Real upstream status is
lost (proxy answers 200 on success). status_code / response_url
check types degrade to "200 if HTML returned, AVAILABLE otherwise".
"""
SESSION_PREFIX_DEFAULT = "maigret"
def __init__(self, *args, **kwargs):
self.logger = kwargs.get('logger', Mock())
config = kwargs.get('config') or {}
self._modules: List[Dict[str, Any]] = []
for raw in config.get('modules') or []:
module = dict(raw)
module.setdefault('method', 'json_api')
module.setdefault('name', module.get('method'))
self._modules.append(module)
if not self._modules:
raise ValueError("CloudflareWebgateChecker requires at least one module")
# Session ID is computed per-request from the target host. Sharing a
# single session across hosts caused FlareSolverr to break in
# practice (TLS state / cookies leaking between domains), so each
# host gets its own Chrome instance.
self._session_prefix = (
f"{config.get('session_prefix', self.SESSION_PREFIX_DEFAULT)}-{os.getpid()}"
)
self.url = None
self.headers = None
self.allow_redirects = True
self.timeout = 0
self.method = 'get'
self.payload = None
@property
def session_id(self) -> str:
"""FlareSolverr session ID, scoped per target host."""
from urllib.parse import urlparse
host = urlparse(self.url or "").hostname or "default"
host_safe = re.sub(r"[^a-zA-Z0-9.-]", "_", host)
return f"{self._session_prefix}-{host_safe}"
def prepare(self, url, headers=None, allow_redirects=True, timeout=0, method='get', payload=None):
self.url = url
self.headers = headers or {}
self.allow_redirects = allow_redirects
self.timeout = timeout
self.method = method
self.payload = payload
return None
async def close(self):
pass
async def check(self) -> Tuple[Optional[str], int, Optional[CheckError]]:
attempts: List[str] = []
last_error: Optional[CheckError] = None
for module in self._modules:
method = module.get('method')
module_name = module.get('name', method or '?')
if method == 'json_api':
result = await self._check_flaresolverr(module)
elif method == 'url_rewrite':
result = await self._check_url_rewrite(module)
else:
self.logger.warning(
f"Webgate module '{module_name}' has unknown method "
f"'{method}', skipping"
)
attempts.append(f"{module_name}:unknown-method")
continue
body, status, err = result
if err is None:
return result
last_error = err
attempts.append(f"{module_name}:{err.type}")
self.logger.info(
f"Webgate module '{module_name}' failed for {self.url}: "
f"{err.type}: {err.desc}. Trying next module if any."
)
# All modules failed. Give the user a single, actionable error with
# the first module's URL — that's almost always FlareSolverr, and
# the most common failure is "user forgot to start the container".
primary = self._modules[0]
primary_url = primary.get('url', '?')
primary_method = primary.get('method', '?')
hint = (
f"docker run -d -p 8191:8191 ghcr.io/flaresolverr/flaresolverr:latest"
if primary_method == 'json_api'
else "start the local proxy container"
)
last_desc = last_error.desc if last_error else "unknown"
return None, 0, CheckError(
"Webgate unavailable",
f"all {len(self._modules)} module(s) failed [{', '.join(attempts)}]. "
f"Last error: {last_desc}. "
f"Is the solver running at {primary_url}? (hint: {hint})",
)
async def _check_flaresolverr(
self, module: Dict[str, Any]
) -> Tuple[Optional[str], int, Optional[CheckError]]:
endpoint = module.get('url') or 'http://localhost:8191/v1'
max_timeout_ms = int(module.get('max_timeout_ms', 60000))
post_method = self.method.lower() == 'post'
cmd = "request.post" if post_method else "request.get"
body: Dict[str, Any] = {
"cmd": cmd,
"url": self.url,
"maxTimeout": max_timeout_ms,
"session": self.session_id,
}
proxy = module.get('proxy')
if isinstance(proxy, str) and proxy:
body["proxy"] = {"url": proxy}
elif isinstance(proxy, dict) and proxy.get("url"):
body["proxy"] = {k: v for k, v in proxy.items() if k in ("url", "username", "password")}
if post_method and self.payload is not None:
# FlareSolverr expects postData as urlencoded string for form data,
# but if site.request_payload is JSON we still send it.
body["postData"] = (
"&".join(f"{k}={quote(str(v))}" for k, v in self.payload.items())
)
timeout = max(int(self.timeout) if self.timeout else 30, max_timeout_ms / 1000 + 5)
try:
async with ClientSession() as session:
async with session.post(
endpoint, json=body, timeout=timeout
) as resp:
if resp.status >= 500:
return None, 0, CheckError(
"Webgate", f"FlareSolverr {resp.status}"
)
data = await resp.json()
except (ClientConnectorError, ServerDisconnectedError) as e:
return None, 0, CheckError("Webgate unreachable", str(e))
except asyncio.TimeoutError:
return None, 0, CheckError("Webgate timeout", endpoint)
except Exception as e:
self.logger.debug(e, exc_info=True)
return None, 0, CheckError("Webgate", str(e))
if data.get("status") != "ok":
return None, 0, CheckError("Webgate", data.get("message", "unknown"))
solution = data.get("solution") or {}
upstream_status = int(solution.get("status") or 0)
response_text = solution.get("response") or ""
# Diagnostic: warn if FlareSolverr returned the CF challenge page
# itself (challenge not fully solved) rather than the real content.
# When this happens with sites that have weak presenseStrs/absenceStrs,
# maigret's default-true presence rule produces false CLAIMED.
cf_markers = ("Just a moment", "_cf_chl_opt", "cf-mitigated", "challenges.cloudflare.com")
if response_text and any(m in response_text for m in cf_markers):
self.logger.warning(
f"Webgate response from {self.url} still contains CF challenge "
f"markers (status={upstream_status}, body={len(response_text)}b). "
f"FlareSolverr likely did not solve the challenge — site checks "
f"with weak markers may produce false CLAIMED."
)
self.logger.info(
f"Webgate response: url={self.url} status={upstream_status} "
f"body_len={len(response_text)}"
)
return response_text, upstream_status, None
async def _check_url_rewrite(
self, module: Dict[str, Any]
) -> Tuple[Optional[str], int, Optional[CheckError]]:
url_template = module.get('url') or ''
if "{url}" not in url_template:
return None, 0, CheckError(
"Webgate", f"module '{module.get('name')}' url has no {{url}} placeholder"
)
from urllib.parse import quote_plus
proxy_url = url_template.format(url=quote_plus(self.url))
timeout = self.timeout if self.timeout else 30
try:
async with ClientSession() as session:
async with session.get(proxy_url, timeout=timeout) as resp:
if resp.status >= 500:
return None, 0, CheckError(
"Webgate", f"url_rewrite proxy {resp.status}"
)
body = await resp.text()
except (ClientConnectorError, ServerDisconnectedError) as e:
return None, 0, CheckError("Webgate unreachable", str(e))
except asyncio.TimeoutError:
return None, 0, CheckError("Webgate timeout", proxy_url)
except Exception as e:
self.logger.debug(e, exc_info=True)
return None, 0, CheckError("Webgate", str(e))
# url_rewrite mode CANNOT recover the upstream HTTP status.
# We assume 200 when HTML is returned; status_code/response_url
# check types will misfire (see docs).
return body, 200, None
class CheckerMock:
def __init__(self, *args, **kwargs):
pass
@@ -715,7 +452,7 @@ def process_site_result(
MaigretCheckStatus.UNKNOWN,
query_time=response_time,
error=check_error,
context=str(check_error),
context=str(CheckError),
tags=fulltags,
)
elif check_type == "message":
@@ -810,24 +547,9 @@ def make_site_result(
# workaround to prevent slash errors
url = re.sub("(?<!:)/+", "/", url)
# Select checker. Order of precedence:
# 1. Cloudflare webgate (FlareSolverr / CloudflareBypassForScraping) when
# bypass is active and site.protection requests it.
# 2. curl_cffi for sites requiring TLS impersonation.
# 3. Default protocol-specific checker (aiohttp).
cf_bypass = options.get("cloudflare_bypass")
needs_webgate = bool(cf_bypass) and any(
p in cf_bypass["trigger_protection"] for p in site.protection
)
# Select checker: use curl_cffi for sites requiring TLS impersonation
needs_impersonation = 'tls_fingerprint' in site.protection
if needs_webgate:
checker = CloudflareWebgateChecker(logger=logger, config=cf_bypass)
logger.info(
f"Using Cloudflare webgate for {site.name} "
f"(protection: {list(site.protection)})"
)
elif needs_impersonation and CURL_CFFI_AVAILABLE:
if needs_impersonation and CURL_CFFI_AVAILABLE:
checker = CurlCffiChecker(logger=logger, browser_emulate='chrome')
elif needs_impersonation and not CURL_CFFI_AVAILABLE:
logger.warning(
@@ -1039,7 +761,6 @@ async def maigret(
cookies=None,
retries=0,
check_domains=False,
cloudflare_bypass: Optional[Dict[str, Any]] = None,
*args,
**kwargs,
) -> QueryResultWrapper:
@@ -1138,7 +859,6 @@ async def maigret(
options["timeout"] = timeout
options["id_type"] = id_type
options["forced"] = forced
options["cloudflare_bypass"] = cloudflare_bypass
# results from analysis of all sites
all_results: Dict[str, QueryResultWrapper] = {}
@@ -1242,7 +962,6 @@ async def site_self_check(
cookies=None,
auto_disable=False,
diagnose=False,
cloudflare_bypass: Optional[Dict[str, Any]] = None,
):
"""
Self-check a site configuration.
@@ -1283,7 +1002,6 @@ async def site_self_check(
tor_proxy=tor_proxy,
i2p_proxy=i2p_proxy,
cookies=cookies,
cloudflare_bypass=cloudflare_bypass,
)
# don't disable entries with other ids types
@@ -1412,7 +1130,6 @@ async def self_check(
auto_disable=False,
diagnose=False,
no_progressbar=False,
cloudflare_bypass: Optional[Dict[str, Any]] = None,
) -> dict:
"""
Run self-check on sites.
@@ -1441,8 +1158,7 @@ async def self_check(
for _, site in all_sites.items():
check_coro = site_self_check(
site, logger, sem, db, silent, proxy, tor_proxy, i2p_proxy,
skip_errors=True, auto_disable=auto_disable, diagnose=diagnose,
cloudflare_bypass=cloudflare_bypass,
skip_errors=True, auto_disable=auto_disable, diagnose=diagnose
)
future = asyncio.ensure_future(check_coro)
tasks.append((site.name, future))
+1 -37
View File
@@ -13,19 +13,7 @@ from argparse import ArgumentParser, RawDescriptionHelpFormatter
from typing import List, Tuple
import os.path as path
try:
from socid_extractor import extract, parse
except ImportError as e:
raise ImportError(
"Missing dependency: socid_extractor\n\n"
"If installed from PyPI:\n"
" pip install -U maigret\n\n"
"If running from a cloned repository:\n"
" pip install -e .\n\n"
"Then run Maigret as:\n"
" python -m maigret <username>"
) from e
from socid_extractor import extract, parse # type: ignore[import-not-found]
from .__version__ import __version__
from .checking import (
@@ -34,7 +22,6 @@ from .checking import (
self_check,
BAD_CHARS,
maigret,
build_cloudflare_bypass_config,
)
from . import errors
from .notify import QueryNotifyPrint
@@ -282,13 +269,6 @@ def setup_arguments_parser(settings: Settings):
default=settings.domain_search,
help="Enable (experimental) feature of checking domains on usernames.",
)
parser.add_argument(
"--cloudflare-bypass",
action="store_true",
default=False,
help="Enable Cloudflare webgate bypass for sites with protection cf_js_challenge / cf_firewall / webgate. "
"Requires a local CloudflareBypassForScraping instance (see settings.json -> cloudflare_bypass.modules[0].url).",
)
filter_group = parser.add_argument_group(
'Site filtering', 'Options to set site search scope'
@@ -560,20 +540,6 @@ async def main():
arg_parser = setup_arguments_parser(settings)
args = arg_parser.parse_args()
# Resolve Cloudflare webgate config (CLI flag OR settings.cloudflare_bypass.enabled)
cf_bypass_config = build_cloudflare_bypass_config(
settings, force_enable=args.cloudflare_bypass
)
if cf_bypass_config:
modules_summary = ", ".join(
f"{m.get('name', m.get('method'))}({m.get('url')})"
for m in cf_bypass_config["modules"]
)
logger.info(
f"Cloudflare webgate active: triggers={cf_bypass_config['trigger_protection']}, "
f"modules=[{modules_summary}]"
)
# Re-set logging level based on args
if args.debug:
log_level = logging.DEBUG
@@ -704,7 +670,6 @@ async def main():
auto_disable=args.auto_disable,
diagnose=args.diagnose,
no_progressbar=args.no_progressbar,
cloudflare_bypass=cf_bypass_config,
)
is_need_update = check_result.get('needs_update', False)
@@ -839,7 +804,6 @@ async def main():
no_progressbar=args.no_progressbar,
retries=args.retries,
check_domains=args.with_domains,
cloudflare_bypass=cf_bypass_config,
)
if not args.ai:
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -1,8 +1,8 @@
{
"version": 1,
"updated_at": "2026-05-09T07:59:17Z",
"sites_count": 3154,
"updated_at": "2026-04-29T14:56:55Z",
"sites_count": 3157,
"min_maigret_version": "0.6.0",
"data_sha256": "acf9d9fef8412bf05fa09d50c1ae363e5c8394597b1aaa3f98a9a1c4e31ca356",
"data_sha256": "5dac8f1c045ea650d5872cf9dfd7f224410eaadba0f2b7eb60514cc51ba0097a",
"data_url": "https://raw.githubusercontent.com/soxoj/maigret/main/maigret/resources/data.json"
}
+1 -21
View File
@@ -61,25 +61,5 @@
"web_interface_port": 5000,
"no_autoupdate": false,
"db_update_meta_url": "https://raw.githubusercontent.com/soxoj/maigret/main/maigret/resources/db_meta.json",
"autoupdate_check_interval_hours": 24,
"cloudflare_bypass": {
"enabled": false,
"session_prefix": "maigret",
"trigger_protection": ["cf_js_challenge", "cf_firewall", "webgate"],
"modules": [
{
"name": "flaresolverr",
"method": "json_api",
"url": "http://localhost:8191/v1",
"max_timeout_ms": 60000,
"comment": "FlareSolverr (https://github.com/FlareSolverr/FlareSolverr). docker run -d -p 8191:8191 ghcr.io/flaresolverr/flaresolverr:latest"
},
{
"name": "chrome_webgate",
"method": "url_rewrite",
"url": "http://localhost:8000/html?url={url}&retries=1",
"comment": "CloudflareBypassForScraping fallback. WARNING: returns rendered HTML only — checkType: status_code and response_url misfire."
}
]
}
"autoupdate_check_interval_hours": 24
}
-1
View File
@@ -47,7 +47,6 @@ class Settings:
no_autoupdate: bool
db_update_meta_url: str
autoupdate_check_interval_hours: int
cloudflare_bypass: dict
# submit mode settings
presence_strings: list
+3 -19
View File
@@ -181,15 +181,7 @@ class MaigretSite:
if self.url_regexp:
match_groups = self.url_regexp.match(url)
if match_groups:
username = next(
(
group.rstrip("/")
for group in reversed(match_groups.groups())
if isinstance(group, str) and group
),
None,
)
return username
return match_groups.groups()[-1].rstrip("/")
return None
@@ -204,16 +196,8 @@ class MaigretSite:
match_groups = self.url_regexp.match(url)
if not match_groups:
return None
_id = next(
(
group.rstrip("/")
for group in reversed(match_groups.groups())
if isinstance(group, str) and group
),
None,
)
if _id is None:
return None
_id = match_groups.groups()[-1].rstrip("/")
_type = self.type
return _id, _type
-1
View File
@@ -113,7 +113,6 @@ class Submitter:
cookies=self.args.cookie_file,
# Don't skip errors in submit mode - we need check both false positives/true negatives
skip_errors=False,
cloudflare_bypass=getattr(self, 'cloudflare_bypass', None),
)
return changes
Generated
+247 -318
View File
@@ -275,47 +275,6 @@ files = [
{file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"},
]
[[package]]
name = "ast-serialize"
version = "0.3.0"
description = "Python bindings for mypy AST serialization"
optional = false
python-versions = ">=3.7"
groups = ["dev"]
files = [
{file = "ast_serialize-0.3.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:3a867927df59f76a18dc1d874a0b2c079b42c58972dca637905576deb0912e14"},
{file = "ast_serialize-0.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a6fb063bf040abf8321e7b8113a0554eda445ffc508aa51287f8808886a5ae22"},
{file = "ast_serialize-0.3.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5075cd8482573d743586779e5f9b652a015e37d4e95132d7e5a9bc5c8f483d8f"},
{file = "ast_serialize-0.3.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:41560b27794f4553b0f77811e9fb325b77db4a2b39018d437e09932275306e66"},
{file = "ast_serialize-0.3.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b967c01ca74909c5d90e0fe4393401e2cc5da5ebd9a6262a19e45ffd3757dec8"},
{file = "ast_serialize-0.3.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:424ebb8f46cd993f7cec4009d119312d8433dd90e6b0df0499cd2c91bdcc5af9"},
{file = "ast_serialize-0.3.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d14b1d566b56e2ee70b11fec1de7e0b94ec7cd83717ec7d189967841a361190e"},
{file = "ast_serialize-0.3.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7ba30b18735f047ec11103d1ab92f4789cf1fea1e0dc89b04a2f5a0632fd79de"},
{file = "ast_serialize-0.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e6ea0754cb7b0f682ebb005ffb0d18f8d17993490d9c289863cd69cacc4ab8df"},
{file = "ast_serialize-0.3.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:a0c5aa1073a5ba7b2abaa4b54abe8b8d75c4d1e2d54a2ff70b0ca6222fea5728"},
{file = "ast_serialize-0.3.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:4e52650d834c1ea7791969a361de2c54c13b2fb4c519ec79445fa8b9021a147d"},
{file = "ast_serialize-0.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:15bd6af3f136c61dae27805eb6b8f3269e85a545c4c27ffe9e530ead78d2b36d"},
{file = "ast_serialize-0.3.0-cp314-cp314t-win32.whl", hash = "sha256:d188bfe37b674b49708497683051d4b571366a668799c9b8e8a94513694969d9"},
{file = "ast_serialize-0.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:5832c2fdf8f8a6cf682b4cfcf677f5eaf39b4ddbc490f5480cfccdd1e7ce8fa1"},
{file = "ast_serialize-0.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:670f177188d128fb7f9f15b5ad0e1b553d22c34e3f584dcb83eb8077600437f0"},
{file = "ast_serialize-0.3.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:2ec2fafa5e4313cc8feed96e436ebe19ac7bc6fa41fbc2827e826c48b9e4c3a9"},
{file = "ast_serialize-0.3.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:ef6d3c08b7b4cd29b48410338e134764a00e76d25841eb02c1084e868c888ecc"},
{file = "ast_serialize-0.3.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d841424f41b886e98044abc80769c14a956e6e5ccd5fb5b0d9f5ead72be18a4"},
{file = "ast_serialize-0.3.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d21453734ad39367ede5d37efe4f59f830ce1c09f432fc72a90e368f77a4a3e7"},
{file = "ast_serialize-0.3.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f5e110cdce2a347e1dd987529c88ef54d26f67848dce3eba1b3b2cc2cf085c94"},
{file = "ast_serialize-0.3.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b6e23a98e57560a055f5c4b68700a0fd5ce483d2814c23140b3638c7f5d1e61"},
{file = "ast_serialize-0.3.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1c9e763d70293d65ce1e1ea8c943140c68d0953f0268c7ee0998f2e07f77dd0"},
{file = "ast_serialize-0.3.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4388a1796c228f1ce5c391426f7d21a0003ad3b47f677dbeded9bd1a85c7209f"},
{file = "ast_serialize-0.3.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:5283cdcc0c64c3d8b9b688dc6aaa012d9c0cf1380a7f774a6bae6a1c01b3205a"},
{file = "ast_serialize-0.3.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:f5ef88cc5842a5d7a6ac09dc0d5fc2c98f5d276c1f076f866d55047ce886785b"},
{file = "ast_serialize-0.3.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:cc14bf402bdc0978594ecce783793de2c7470cd4f5cd7eb286ca97ed8ff7cba9"},
{file = "ast_serialize-0.3.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:11eae0cf1b7b3e0678133cc2daa974ea972caf02eb4b3aa062af6fa9acd52c57"},
{file = "ast_serialize-0.3.0-cp39-abi3-win32.whl", hash = "sha256:2db3dd99de5e6a5a11d7dda73de8750eb6e5baaf25245adf7bdcfe64b6108ae2"},
{file = "ast_serialize-0.3.0-cp39-abi3-win_amd64.whl", hash = "sha256:a2cd125adccf7969470621905d302750cd25951f22ea430d9a25b7be031e5549"},
{file = "ast_serialize-0.3.0-cp39-abi3-win_arm64.whl", hash = "sha256:0dd00da29985f15f50dc35728b7e1e7c84507bccfea1d9914738530f1c72238a"},
{file = "ast_serialize-0.3.0.tar.gz", hash = "sha256:1bc3ca09a63a021376527c4e938deedd11d11d675ce850e6f9c7487f5889992b"},
]
[[package]]
name = "asttokens"
version = "3.0.0"
@@ -1437,103 +1396,103 @@ testing = ["PyYAML", "atheris (>=2.3.0,<2.4.0) ; python_version < \"3.12\"", "bs
[[package]]
name = "librt"
version = "0.10.0"
version = "0.8.1"
description = "Mypyc runtime library"
optional = false
python-versions = ">=3.9"
groups = ["dev"]
markers = "platform_python_implementation != \"PyPy\""
files = [
{file = "librt-0.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7dc99f9642100b86e5f6bb14cdc9970009e31a9ef7d64df6704b7018451524a3"},
{file = "librt-0.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8298cedfcfaff3790000bd057aaaa3df1b0ab54cf7b48eeab16184cbb1bc66b9"},
{file = "librt-0.10.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee7dbe312dbf76468255b79a7ba311236fde620f2f7055fc09d421e31340314e"},
{file = "librt-0.10.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:56ed90c48c19249012dadfd79a1bc13bd5168ea60a70722d330a3a600c0b1852"},
{file = "librt-0.10.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d74ca0f4b2b09c117f913d4df01f6b934dff8a271096b35167d5264a31649f0"},
{file = "librt-0.10.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8eb2daa9375f93c0e55ff5e44a4bbe98f39e5fe52e1abf9c97acb67743b61bf8"},
{file = "librt-0.10.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7b09b90e634e6dff57978cd358070046071e2b120501f10787aeb35425f504f6"},
{file = "librt-0.10.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:2cf22fd379d60c739b800d4295ed34045f8b04aa8df9c12bd2f8f43f7fe672b7"},
{file = "librt-0.10.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:74c798793fcf29a84d442278ebe0bb1fff79fe58ac4106eeff7019cbba861423"},
{file = "librt-0.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:dc4f1573401e8dbe6c26511fe027620b0fb30ae9a7ab814e02e510626b8b5f9c"},
{file = "librt-0.10.0-cp310-cp310-win32.whl", hash = "sha256:e1428275f5fe3d4db6822e58d8b005a5b28ffca55e8433ebc051247fbe46429f"},
{file = "librt-0.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:0708e9408f585b0f065081680583a577652099680ccf820c7538904322b679c3"},
{file = "librt-0.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:01b4500ca3a625450c032a9142a8e843923ce263fa8a92ad1b38927cabe2fe72"},
{file = "librt-0.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6b7e42d1b3e300d20bfc87e72ffd62f0a92a2cb3c35f7bf90df90c9d2a49f74c"},
{file = "librt-0.10.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c8ef7b8c61ce3a1b597cd3e15348ff1574325165c2e7ce09a718154cde2a7950"},
{file = "librt-0.10.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:e73c84f72d1fa0d6eaa7a1930b436ba8d2c90c58d77bfabb09995a69ad35f6c0"},
{file = "librt-0.10.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9728cb98713bd862fb8f4fd6a642d1896c86058a41d77c70f3d5cee75e725275"},
{file = "librt-0.10.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:648b7e941d20acd72f9652115e0e53facd98156d61f9ebf7a812bdef8bdccea9"},
{file = "librt-0.10.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c3e33747c068e86a9007c20fdb777eb5ba8d3d19136d7812f88e69a713041b6f"},
{file = "librt-0.10.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d509c745bf7e77d1107cf05e6abb249dc03fad13eb39f2286a49deedaeb2bcd7"},
{file = "librt-0.10.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:786ad5a15e99d0e0e74f3adbeecc198a5ac58f340be07e984723d1e0074838de"},
{file = "librt-0.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:075582d877a97ee3d8e77bda3689dbe617b14f6469224a2d80b4b6c38e3951aa"},
{file = "librt-0.10.0-cp311-cp311-win32.whl", hash = "sha256:75ecdc3f5a90065aa2af2e574706c5495adc392520762dcf10b1aa716f0b8090"},
{file = "librt-0.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:b6f6084884131d8a52cb9d7095ff2aa52c1e786d9fdaefab1fb4515415e9e083"},
{file = "librt-0.10.0-cp311-cp311-win_arm64.whl", hash = "sha256:0140bd62151160047e89b2730cb6f8506cdac5127baa1afb9231e4dd3fe7f681"},
{file = "librt-0.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b4b58a44b407e91f633dafee008de9ddea6aa2a555ed94929c099260910bd0ba"},
{file = "librt-0.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:950b79b11762531bdf45a9df909d2f9a2a8445c70c88665c01d14c8511a27dc5"},
{file = "librt-0.10.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4538453f51be197633b425912c150e25b0667252d3741c53e8368176d98d9d37"},
{file = "librt-0.10.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:70b955f091beac93e994a0b7ec616934f63b3ea5c3d6d7af847562f935aceca7"},
{file = "librt-0.10.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:483e685e06b6163728ba6c85d74315176be7190f432ec2a41226e5e14355d5f0"},
{file = "librt-0.10.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7ac53d946a009d1a38c44a60812708c9458fb2a239a5f630d8e625571386650f"},
{file = "librt-0.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bc8771c9fcf0ea894ca41fdc2abd83572c2fbda221f232d86e718614e57ff513"},
{file = "librt-0.10.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:70805dbc5257892ac572f86290a61e3c8d90224ecce1a8b2d1f7ed51965417f4"},
{file = "librt-0.10.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d3b4f300f7bcba6e2ff73fb8bef1898479e9772bfa2682998c636391633ec826"},
{file = "librt-0.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:943bc943f92f4fb3408fae62485c6a3ad68ce4f2ee205643a39641525c19a276"},
{file = "librt-0.10.0-cp312-cp312-win32.whl", hash = "sha256:6065c1a758fba1010b41401013903d3d5d2750eab425ddedd584abac31d0630e"},
{file = "librt-0.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:d788ecbe208ab352dab0e105cc06057bf9a2fc7e58cabb0d751ad9e30062b9e2"},
{file = "librt-0.10.0-cp312-cp312-win_arm64.whl", hash = "sha256:6003d1f295bdba02656dc81308208fc060d0a51d8c0d0a6db70f7f3c57b9ba0a"},
{file = "librt-0.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f0ede79d682e73f91c1b599a76d78b7464b9b5d213754cedb13372d9df36e596"},
{file = "librt-0.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e0ba0b131fdb336c8b9c948e397f4a7e649d0f783b529f07b647bf4961df392e"},
{file = "librt-0.10.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2728117da2afb96fb957768725ee43dc9a2d73b031e02da424b818a3cdd3a275"},
{file = "librt-0.10.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:723ba80594c49cdf0584196fc430752262605dc9449902fc9bd3d9b79976cb77"},
{file = "librt-0.10.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7292edaaca294a61a978c53a3c7d6130d099b0dfbc8f0a65916cdc6b891b9852"},
{file = "librt-0.10.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:89fe9d539f2c10a1666633eeeac507ce95dd06d9ecc58de3c6390dba156a3d3a"},
{file = "librt-0.10.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4efa7b9587503fa5b67f40593302b9c8836d211d222ff9f7cafe67be5f8f0b10"},
{file = "librt-0.10.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:22dc982ef59df0136df36092ccbdbb570ced8aafb33e49585739b2f1de1c13b6"},
{file = "librt-0.10.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:6f2e5f3606253a84cea719c94a3bb1c54487b5d617d0254d46e0920d8a06be3f"},
{file = "librt-0.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:40884bfaa1e29f6b6a9be255007d8f359bfc9e61d68bdef8ed3158bfcbc95df9"},
{file = "librt-0.10.0-cp313-cp313-win32.whl", hash = "sha256:3cd34cd8254eba756660bff6c2da91278248184301054fe3e4feb073bdd49b14"},
{file = "librt-0.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:7baac5313e2d8dce1386f97777a8d03ab28f5fe1e780b3b9ac2ee7544551fedc"},
{file = "librt-0.10.0-cp313-cp313-win_arm64.whl", hash = "sha256:afc5b4406c8e2515698d922a5c7823a009312835ea58196671fff40e35cb8166"},
{file = "librt-0.10.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:f09588a30e6a22ec624090d72a3ab1a6d4d5485c3ed739603e76aa3c16efa688"},
{file = "librt-0.10.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:131ade118d12bd7a0adc4e655474a553f1b76cf78385868885944d21d51e45e0"},
{file = "librt-0.10.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8b9ab28e40d011c373a189eae900c916e66d6fbecf7983e9e4883089ee085ef"},
{file = "librt-0.10.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:67c39bb30da73bae1f293d1ed8bc2f8f6642649dd0928d3600aeff3041ac23d6"},
{file = "librt-0.10.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8c3273c6b774614f093c8927c2bf1b077d0fefde988fe98f46a333734e5597ab"},
{file = "librt-0.10.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9dd7c1b86a4baa583ab5db977484b93a2c474e69e96ef3e9538387ea54229cb9"},
{file = "librt-0.10.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a77385c5a202e831149f7ad03be9e67cf80e957e52c614e83dcb822c95222eb8"},
{file = "librt-0.10.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c6a5eafa74b5655bad59886138ed68426f098a6beb8cb95a71f2cc3cd8bb33fe"},
{file = "librt-0.10.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:1fc93d0439204c50ab4d1512611ce2c206f1b369b419f69c7c27c761561e3291"},
{file = "librt-0.10.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:79e713c178bc7a744adfbee6b4619a288eecc0c914da2a9313a20255abe2f0cf"},
{file = "librt-0.10.0-cp314-cp314-win32.whl", hash = "sha256:2eba9d955a68c41d9f326be3da42f163ec3518b7ab20f1c826224e7bed71e0bf"},
{file = "librt-0.10.0-cp314-cp314-win_amd64.whl", hash = "sha256:cbfaf7f5145e9917f5d18bffa298eff6a19d74e7b8b11dabdca95785befe8dbf"},
{file = "librt-0.10.0-cp314-cp314-win_arm64.whl", hash = "sha256:8d6d385d1969849a6b1397114df22714b6ded917bada98668e3e974dc663477e"},
{file = "librt-0.10.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:6c3a82d3bd32631ef5c79922dfc028520c9ad840255979ab4d908271818039ee"},
{file = "librt-0.10.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d64cc66005dc324c9bb1fa3fc2841f529002f6eb15966d55e46d430f56955a6a"},
{file = "librt-0.10.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9bb562cd28c88cd2c6a9a6c78f99dc39348d6b16c94adc25de0e574acf1176e9"},
{file = "librt-0.10.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:b809aa2854d019c28773b03605df22adc675ee4f3f4402d673581313e8906119"},
{file = "librt-0.10.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cc15acabdd519bd4176fdadc2119e5e3093485d86f89138daf47e5b4cedb983a"},
{file = "librt-0.10.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b1b2d835307d08ddadd94568e2369648ec9173bd3eea6d7f52a1abe717c81f98"},
{file = "librt-0.10.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d261c6a2f93335a5167887fb0223e8b98ffce20ee3fde242e8e58a37ece6d0e5"},
{file = "librt-0.10.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:e2ffd44963f8e7f68995504d90f9881d64e94dc1d8e310039b9526108fc0c0f7"},
{file = "librt-0.10.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:5f285f6455ed495791c4d8630e5af732960adea93cac4c893d15619f2eae53e8"},
{file = "librt-0.10.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f6034ff52e663d34c7b82ef2aa2f94ad7c1d939e2368e63b06844bc4d127d2e1"},
{file = "librt-0.10.0-cp314-cp314t-win32.whl", hash = "sha256:657860fd877fba6a241ea088ef99f63ca819945d3c715265da670bad56c37ebe"},
{file = "librt-0.10.0-cp314-cp314t-win_amd64.whl", hash = "sha256:56ded2d66010203a0cb5af063b609e3f079531a0e5e576d618dece859fd2e1af"},
{file = "librt-0.10.0-cp314-cp314t-win_arm64.whl", hash = "sha256:1ee63f30abf18ed4830fdbaf87b2b6f4bba1e198d46085c314edde4045e56715"},
{file = "librt-0.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:83628c28545a5f4d860b48fae7f62367c006ab7405898573f34af8b7dcb178a2"},
{file = "librt-0.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4bcf57b4de07e2d4bd093636ee59dc1b64298f304148dd9c4f001f7c7897650d"},
{file = "librt-0.10.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2236c16bdb7c527eb671e4b599eec2c4229fddf80573de2bde529924f46db971"},
{file = "librt-0.10.0-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:c1efa2f494811b245427095225a4d0251aee33ba4cf6ba2b7a6a9a619bc1a2ff"},
{file = "librt-0.10.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d14626d350af79eed4b4f8886530052e3f78a62e9e53d2699f726f99c3d1d122"},
{file = "librt-0.10.0-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b609f3461beae5608ca5219131ae5cdfea2e369818030abfc6ba7086830cde42"},
{file = "librt-0.10.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0e2338b67c8e72755ccd1ab77b027e3701b375a1e12b4576fdefdf9c46448274"},
{file = "librt-0.10.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:17cadff57139ff49beea0b17e50b28dfc3f9687126399696de4d2d8ae86ba7ff"},
{file = "librt-0.10.0-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:5496102c8ed065c128d0f0fd10dcb3f9f3fd9b346954462d62af623f1b1ec7cd"},
{file = "librt-0.10.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:537e1bfa459c1c92a263768a8a0c6fd0558049fa6c1b866d791eea711ae64114"},
{file = "librt-0.10.0-cp39-cp39-win32.whl", hash = "sha256:85aca5a7ddc5f2d4cba24eba35667d83893ff2980dbd5884be16f538a24351e4"},
{file = "librt-0.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:e45e46ff5fdfc690e77bb8557d5ba56974c4006b744ddbd70cce99fec6bfbeb8"},
{file = "librt-0.10.0.tar.gz", hash = "sha256:1aba1e8aa4e3307a7be68a74149545fde7451964dc0235a8bec5704a17bdda42"},
{file = "librt-0.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:81fd938344fecb9373ba1b155968c8a329491d2ce38e7ddb76f30ffb938f12dc"},
{file = "librt-0.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5db05697c82b3a2ec53f6e72b2ed373132b0c2e05135f0696784e97d7f5d48e7"},
{file = "librt-0.8.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d56bc4011975f7460bea7b33e1ff425d2f1adf419935ff6707273c77f8a4ada6"},
{file = "librt-0.8.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdc0f588ff4b663ea96c26d2a230c525c6fc62b28314edaaaca8ed5af931ad0"},
{file = "librt-0.8.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:97c2b54ff6717a7a563b72627990bec60d8029df17df423f0ed37d56a17a176b"},
{file = "librt-0.8.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8f1125e6bbf2f1657d9a2f3ccc4a2c9b0c8b176965bb565dd4d86be67eddb4b6"},
{file = "librt-0.8.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8f4bb453f408137d7581be309b2fbc6868a80e7ef60c88e689078ee3a296ae71"},
{file = "librt-0.8.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c336d61d2fe74a3195edc1646d53ff1cddd3a9600b09fa6ab75e5514ba4862a7"},
{file = "librt-0.8.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:eb5656019db7c4deacf0c1a55a898c5bb8f989be904597fcb5232a2f4828fa05"},
{file = "librt-0.8.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c25d9e338d5bed46c1632f851babf3d13c78f49a225462017cf5e11e845c5891"},
{file = "librt-0.8.1-cp310-cp310-win32.whl", hash = "sha256:aaab0e307e344cb28d800957ef3ec16605146ef0e59e059a60a176d19543d1b7"},
{file = "librt-0.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:56e04c14b696300d47b3bc5f1d10a00e86ae978886d0cee14e5714fafb5df5d2"},
{file = "librt-0.8.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:681dc2451d6d846794a828c16c22dc452d924e9f700a485b7ecb887a30aad1fd"},
{file = "librt-0.8.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3b4350b13cc0e6f5bec8fa7caf29a8fb8cdc051a3bae45cfbfd7ce64f009965"},
{file = "librt-0.8.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ac1e7817fd0ed3d14fd7c5df91daed84c48e4c2a11ee99c0547f9f62fdae13da"},
{file = "librt-0.8.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:747328be0c5b7075cde86a0e09d7a9196029800ba75a1689332348e998fb85c0"},
{file = "librt-0.8.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f0af2bd2bc204fa27f3d6711d0f360e6b8c684a035206257a81673ab924aa11e"},
{file = "librt-0.8.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d480de377f5b687b6b1bc0c0407426da556e2a757633cc7e4d2e1a057aa688f3"},
{file = "librt-0.8.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d0ee06b5b5291f609ddb37b9750985b27bc567791bc87c76a569b3feed8481ac"},
{file = "librt-0.8.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9e2c6f77b9ad48ce5603b83b7da9ee3e36b3ab425353f695cba13200c5d96596"},
{file = "librt-0.8.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:439352ba9373f11cb8e1933da194dcc6206daf779ff8df0ed69c5e39113e6a99"},
{file = "librt-0.8.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:82210adabbc331dbb65d7868b105185464ef13f56f7f76688565ad79f648b0fe"},
{file = "librt-0.8.1-cp311-cp311-win32.whl", hash = "sha256:52c224e14614b750c0a6d97368e16804a98c684657c7518752c356834fff83bb"},
{file = "librt-0.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:c00e5c884f528c9932d278d5c9cbbea38a6b81eb62c02e06ae53751a83a4d52b"},
{file = "librt-0.8.1-cp311-cp311-win_arm64.whl", hash = "sha256:f7cdf7f26c2286ffb02e46d7bac56c94655540b26347673bea15fa52a6af17e9"},
{file = "librt-0.8.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a28f2612ab566b17f3698b0da021ff9960610301607c9a5e8eaca62f5e1c350a"},
{file = "librt-0.8.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:60a78b694c9aee2a0f1aaeaa7d101cf713e92e8423a941d2897f4fa37908dab9"},
{file = "librt-0.8.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:758509ea3f1eba2a57558e7e98f4659d0ea7670bff49673b0dde18a3c7e6c0eb"},
{file = "librt-0.8.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:039b9f2c506bd0ab0f8725aa5ba339c6f0cd19d3b514b50d134789809c24285d"},
{file = "librt-0.8.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bb54f1205a3a6ab41a6fd71dfcdcbd278670d3a90ca502a30d9da583105b6f7"},
{file = "librt-0.8.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:05bd41cdee35b0c59c259f870f6da532a2c5ca57db95b5f23689fcb5c9e42440"},
{file = "librt-0.8.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:adfab487facf03f0d0857b8710cf82d0704a309d8ffc33b03d9302b4c64e91a9"},
{file = "librt-0.8.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:153188fe98a72f206042be10a2c6026139852805215ed9539186312d50a8e972"},
{file = "librt-0.8.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:dd3c41254ee98604b08bd5b3af5bf0a89740d4ee0711de95b65166bf44091921"},
{file = "librt-0.8.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e0d138c7ae532908cbb342162b2611dbd4d90c941cd25ab82084aaf71d2c0bd0"},
{file = "librt-0.8.1-cp312-cp312-win32.whl", hash = "sha256:43353b943613c5d9c49a25aaffdba46f888ec354e71e3529a00cca3f04d66a7a"},
{file = "librt-0.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:ff8baf1f8d3f4b6b7257fcb75a501f2a5499d0dda57645baa09d4d0d34b19444"},
{file = "librt-0.8.1-cp312-cp312-win_arm64.whl", hash = "sha256:0f2ae3725904f7377e11cc37722d5d401e8b3d5851fb9273d7f4fe04f6b3d37d"},
{file = "librt-0.8.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7e6bad1cd94f6764e1e21950542f818a09316645337fd5ab9a7acc45d99a8f35"},
{file = "librt-0.8.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cf450f498c30af55551ba4f66b9123b7185362ec8b625a773b3d39aa1a717583"},
{file = "librt-0.8.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:eca45e982fa074090057132e30585a7e8674e9e885d402eae85633e9f449ce6c"},
{file = "librt-0.8.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c3811485fccfda840861905b8c70bba5ec094e02825598bb9d4ca3936857a04"},
{file = "librt-0.8.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e4af413908f77294605e28cfd98063f54b2c790561383971d2f52d113d9c363"},
{file = "librt-0.8.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5212a5bd7fae98dae95710032902edcd2ec4dc994e883294f75c857b83f9aba0"},
{file = "librt-0.8.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e692aa2d1d604e6ca12d35e51fdc36f4cda6345e28e36374579f7ef3611b3012"},
{file = "librt-0.8.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4be2a5c926b9770c9e08e717f05737a269b9d0ebc5d2f0060f0fe3fe9ce47acb"},
{file = "librt-0.8.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:fd1a720332ea335ceb544cf0a03f81df92abd4bb887679fd1e460976b0e6214b"},
{file = "librt-0.8.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2af9e01e0ef80d95ae3c720be101227edae5f2fe7e3dc63d8857fadfc5a1d"},
{file = "librt-0.8.1-cp313-cp313-win32.whl", hash = "sha256:086a32dbb71336627e78cc1d6ee305a68d038ef7d4c39aaff41ae8c9aa46e91a"},
{file = "librt-0.8.1-cp313-cp313-win_amd64.whl", hash = "sha256:e11769a1dbda4da7b00a76cfffa67aa47cfa66921d2724539eee4b9ede780b79"},
{file = "librt-0.8.1-cp313-cp313-win_arm64.whl", hash = "sha256:924817ab3141aca17893386ee13261f1d100d1ef410d70afe4389f2359fea4f0"},
{file = "librt-0.8.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6cfa7fe54fd4d1f47130017351a959fe5804bda7a0bc7e07a2cdbc3fdd28d34f"},
{file = "librt-0.8.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:228c2409c079f8c11fb2e5d7b277077f694cb93443eb760e00b3b83cb8b3176c"},
{file = "librt-0.8.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7aae78ab5e3206181780e56912d1b9bb9f90a7249ce12f0e8bf531d0462dd0fc"},
{file = "librt-0.8.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:172d57ec04346b047ca6af181e1ea4858086c80bdf455f61994c4aa6fc3f866c"},
{file = "librt-0.8.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6b1977c4ea97ce5eb7755a78fae68d87e4102e4aaf54985e8b56806849cc06a3"},
{file = "librt-0.8.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:10c42e1f6fd06733ef65ae7bebce2872bcafd8d6e6b0a08fe0a05a23b044fb14"},
{file = "librt-0.8.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4c8dfa264b9193c4ee19113c985c95f876fae5e51f731494fc4e0cf594990ba7"},
{file = "librt-0.8.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:01170b6729a438f0dedc4a26ed342e3dc4f02d1000b4b19f980e1877f0c297e6"},
{file = "librt-0.8.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:7b02679a0d783bdae30d443025b94465d8c3dc512f32f5b5031f93f57ac32071"},
{file = "librt-0.8.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:190b109bb69592a3401fe1ffdea41a2e73370ace2ffdc4a0e8e2b39cdea81b78"},
{file = "librt-0.8.1-cp314-cp314-win32.whl", hash = "sha256:e70a57ecf89a0f64c24e37f38d3fe217a58169d2fe6ed6d70554964042474023"},
{file = "librt-0.8.1-cp314-cp314-win_amd64.whl", hash = "sha256:7e2f3edca35664499fbb36e4770650c4bd4a08abc1f4458eab9df4ec56389730"},
{file = "librt-0.8.1-cp314-cp314-win_arm64.whl", hash = "sha256:0d2f82168e55ddefd27c01c654ce52379c0750ddc31ee86b4b266bcf4d65f2a3"},
{file = "librt-0.8.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2c74a2da57a094bd48d03fa5d196da83d2815678385d2978657499063709abe1"},
{file = "librt-0.8.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a355d99c4c0d8e5b770313b8b247411ed40949ca44e33e46a4789b9293a907ee"},
{file = "librt-0.8.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2eb345e8b33fb748227409c9f1233d4df354d6e54091f0e8fc53acdb2ffedeb7"},
{file = "librt-0.8.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9be2f15e53ce4e83cc08adc29b26fb5978db62ef2a366fbdf716c8a6c8901040"},
{file = "librt-0.8.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:785ae29c1f5c6e7c2cde2c7c0e148147f4503da3abc5d44d482068da5322fd9e"},
{file = "librt-0.8.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1d3a7da44baf692f0c6aeb5b2a09c5e6fc7a703bca9ffa337ddd2e2da53f7732"},
{file = "librt-0.8.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5fc48998000cbc39ec0d5311312dda93ecf92b39aaf184c5e817d5d440b29624"},
{file = "librt-0.8.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:e96baa6820280077a78244b2e06e416480ed859bbd8e5d641cf5742919d8beb4"},
{file = "librt-0.8.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:31362dbfe297b23590530007062c32c6f6176f6099646bb2c95ab1b00a57c382"},
{file = "librt-0.8.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cc3656283d11540ab0ea01978378e73e10002145117055e03722417aeab30994"},
{file = "librt-0.8.1-cp314-cp314t-win32.whl", hash = "sha256:738f08021b3142c2918c03692608baed43bc51144c29e35807682f8070ee2a3a"},
{file = "librt-0.8.1-cp314-cp314t-win_amd64.whl", hash = "sha256:89815a22daf9c51884fb5dbe4f1ef65ee6a146e0b6a8df05f753e2e4a9359bf4"},
{file = "librt-0.8.1-cp314-cp314t-win_arm64.whl", hash = "sha256:bf512a71a23504ed08103a13c941f763db13fb11177beb3d9244c98c29fb4a61"},
{file = "librt-0.8.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3dff3d3ca8db20e783b1bc7de49c0a2ab0b8387f31236d6a026597d07fcd68ac"},
{file = "librt-0.8.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:08eec3a1fc435f0d09c87b6bf1ec798986a3544f446b864e4099633a56fcd9ed"},
{file = "librt-0.8.1-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e3f0a41487fd5fad7e760b9e8a90e251e27c2816fbc2cff36a22a0e6bcbbd9dd"},
{file = "librt-0.8.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bacdb58d9939d95cc557b4dbaa86527c9db2ac1ed76a18bc8d26f6dc8647d851"},
{file = "librt-0.8.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6d7ab1f01aa753188605b09a51faa44a3327400b00b8cce424c71910fc0a128"},
{file = "librt-0.8.1-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4998009e7cb9e896569f4be7004f09d0ed70d386fa99d42b6d363f6d200501ac"},
{file = "librt-0.8.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2cc68eeeef5e906839c7bb0815748b5b0a974ec27125beefc0f942715785b551"},
{file = "librt-0.8.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:0bf69d79a23f4f40b8673a947a234baeeb133b5078b483b7297c5916539cf5d5"},
{file = "librt-0.8.1-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:22b46eabd76c1986ee7d231b0765ad387d7673bbd996aa0d0d054b38ac65d8f6"},
{file = "librt-0.8.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:237796479f4d0637d6b9cbcb926ff424a97735e68ade6facf402df4ec93375ed"},
{file = "librt-0.8.1-cp39-cp39-win32.whl", hash = "sha256:4beb04b8c66c6ae62f8c1e0b2f097c1ebad9295c929a8d5286c05eae7c2fc7dc"},
{file = "librt-0.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:64548cde61b692dc0dc379f4b5f59a2f582c2ebe7890d09c1ae3b9e66fa015b7"},
{file = "librt-0.8.1.tar.gz", hash = "sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73"},
]
[[package]]
@@ -2026,61 +1985,60 @@ typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.11\""}
[[package]]
name = "mypy"
version = "2.0.0"
version = "1.20.2"
description = "Optional static typing for Python"
optional = false
python-versions = ">=3.10"
groups = ["dev"]
files = [
{file = "mypy-2.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:65d6f22d643bccaeb182d41d2a9f0990a05a871673c4ae3f97d4931eca0d2294"},
{file = "mypy-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:106650bce72114f43019bf72197296f51c2cd47adfa9d073ea2976c247a404c5"},
{file = "mypy-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c734b7eb89a4cc4ec347f8187ffa730e2b59693407bc93dcb878183037f80a17"},
{file = "mypy-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd9e60388944d0f1432a2419ab938a78d5658df1d143a7172cfe1a197276cf49"},
{file = "mypy-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f95e3890666c3be41af7a7179f4872341c08e90c161ba8e7a08a21f9be92c131"},
{file = "mypy-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:e8e8709ce1b1046b8aad77a506dd01491157102dd727128c0b374b5025c7d769"},
{file = "mypy-2.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:0165968759c99ab79dc1a9f8aaec18e93a1bedcf7c13edd70e68dd3d5faf17cb"},
{file = "mypy-2.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c17b7222e9fdfd352e61fb3131da117e55cc465f701ff232f1bd97a02bbad91f"},
{file = "mypy-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cc0a61adea1a5ffc2d47a4dc4bb180d8103f477fc2a90a1cdcbb168c2cc6caff"},
{file = "mypy-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8578f857b519993d065e5805290b71467ebfae772407a5f57e823755e4fdb850"},
{file = "mypy-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:33f668a37a650df60f7b825c1ac61e6baadd4ac3c89519e929badde58d28edf5"},
{file = "mypy-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29ea6da86c8c5e9addd48fa6e624f467341b3814f54ded871b28980468686dea"},
{file = "mypy-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:904baa0124ebbccf0c7ba94f722cf9186ee30478f5e5b11432ffc8929248ee55"},
{file = "mypy-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:440165501295e523bf1e5d3e411b62b367b901c65610938e75f0e56ba0462461"},
{file = "mypy-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:660790551c988e69d8bf7a35c8b4149edeb22f4a339165702be843532e9dcdb5"},
{file = "mypy-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7a15bf92cd8781f8e72f69ffa7e30d1f434402d065ee1ecd5223ef2ef100f914"},
{file = "mypy-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4ff370b43d7def05bbcd2f5267f0bcda72dd6a552ef2ea9375b02d6fe06da270"},
{file = "mypy-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:37bd246590a018e5a11703b7b09c39d47ede3df5ba3fa863c5b8590b465beb01"},
{file = "mypy-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cce87e92214fac8bf8feb8a680d0c1b6fb748d50e9b57fbb13e4b1d83a3ed19b"},
{file = "mypy-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:e19e9cb69b66a4141009d24898259914fa2b71d026de0b46edf9fafdbf4fd46e"},
{file = "mypy-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:b021614cb08d44785b025982163ec3c39c94bff766ead071fa9e82b4ef6f62cd"},
{file = "mypy-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9ef5f581b61240d1cc629b12f8df6565ed6ffac0d82ed745eef7833222ab50b9"},
{file = "mypy-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:20e3470a165dbc249bdfbe8d1c5172727ef22688cffc279f8c3aa264ab9d4d9a"},
{file = "mypy-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:224ba142eee8b4d65d4db657cb1fc22abec30b135ded6ab297302ba1f62e505d"},
{file = "mypy-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e879ad8a03908ff74d15e8a9b42bf049918e6798d52c011011f1873d0b5877e"},
{file = "mypy-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:65c5c15bcbd18d6fe927cc55c459597a3517d69cc3123f067be3b020010e115e"},
{file = "mypy-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:d1a068acd7c9fb77e9f8923f1556f2f49d6d7895821121b8d97fa5642b9c52f5"},
{file = "mypy-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:ef9d96da1ddffbc21f27d3939319b6846d12393baa17c4d2f3e81e040e73ce2c"},
{file = "mypy-2.0.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c918c64e8ce36557851b0347f84eb12f1965d3a06813c36df253eb0c0afd1d82"},
{file = "mypy-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:301f1a8ccc7d79b542ee218b28bb49443a83e194eb3d10da63ff1649e5aa5d34"},
{file = "mypy-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fdf4ef489d44ce350bac3fd699907834e551d4c934e9cc862ef201215ab1558d"},
{file = "mypy-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9cde2d0989f912fc850890f727d0d76495e7a6c5bdd9912a1efdb64952b4398d"},
{file = "mypy-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cdf05693c231a14fe37dbfce192a3a1372c26a833af4a80f550547742952e719"},
{file = "mypy-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:73aee2da33a2237e66cbe84a94780e53599847e86bb3aa7b93e405e8cd9905f2"},
{file = "mypy-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:1f6dcd8f39971f41edab2728c877c4ac8b50ad3c387ff2770423b79a05d23910"},
{file = "mypy-2.0.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:a04e980b9275c76159da66c6e1723c7798306f9802b31bdaf9358d0c84030ce8"},
{file = "mypy-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:33f9cf4825469b2bc73c53ba55f6d9a9b4cdb60f9e6e228745581520f29b8771"},
{file = "mypy-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:191675c3c7dc2a5c7722a035a6909c277f14046c5e4e02aa5fbf65f8524f08ad"},
{file = "mypy-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c3d26c4321a3b06fc9f04c741e0733af693f82d823f8e64e47b2e63b7f19fa84"},
{file = "mypy-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:bbcbc4d5917ca6ce12de70e051de7f533e3bf92d548b41a38a2232a6fe356525"},
{file = "mypy-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:dbc6ba6d40572ae49268531565793a8f07eac7fc65ad76d482c9b4c8765b6043"},
{file = "mypy-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:77926029dfcb7e1a3ecb0acb2ddbb24ca36be03f7d623e1759ad5376be8f6c01"},
{file = "mypy-2.0.0-py3-none-any.whl", hash = "sha256:8a92b2be3146b4fa1f062af7eb05574cbf3e6eb8e1f14704af1075423144e4e5"},
{file = "mypy-2.0.0.tar.gz", hash = "sha256:1a9e3900ac5c40f1fe813506c7739da6e6f0eab2729067ebd94bfb0bbba53532"},
{file = "mypy-1.20.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cf5a4db6dca263010e2c7bff081c89383c72d187ba2cf4c44759aac970e2f0c4"},
{file = "mypy-1.20.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7b0e817b518bff7facd7f85ea05b643ad8bdcce684cf29784987b0a7c8e1f997"},
{file = "mypy-1.20.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97d7b9a485b40f8ca425460e89bf1da2814625b2da627c0dcc6aa46c92631d14"},
{file = "mypy-1.20.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1e1c12f6d2db3d78b909b5f77513c11eb7f2dd2782b96a3ab6dffc7d44575c99"},
{file = "mypy-1.20.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:89dce27e142d25ffbc154c1819383b69f2e9234dc4ed4766f42e0e8cb264ab5c"},
{file = "mypy-1.20.2-cp310-cp310-win_amd64.whl", hash = "sha256:f376e37f9bf2a946872fc5fd1199c99310748e3c26c7a26683f13f8bdb756cbd"},
{file = "mypy-1.20.2-cp310-cp310-win_arm64.whl", hash = "sha256:6e2b469efd811707bc530fd1effef0f5d6eebcb7fe376affae69025da4b979a2"},
{file = "mypy-1.20.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4077797a273e56e8843d001e9dfe4ba10e33323d6ade647ff260e5cd97d9758c"},
{file = "mypy-1.20.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cdecf62abcc4292500d7858aeae87a1f8f1150f4c4dd08fb0b336ee79b2a6df3"},
{file = "mypy-1.20.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c566c3a88b6ece59b3d70f65bedef17304f48eb52ff040a6a18214e1917b3254"},
{file = "mypy-1.20.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0deb80d062b2479f2c87ae568f89845afc71d11bc41b04179e58165fd9f31e98"},
{file = "mypy-1.20.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bba9ad231e92a3e424b3e56b65aa17704993425bba97e302c832f9466bb85bac"},
{file = "mypy-1.20.2-cp311-cp311-win_amd64.whl", hash = "sha256:baf593f2765fa3a6b1ef95807dbaa3d25b594f6a52adcc506a6b9cb115e1be67"},
{file = "mypy-1.20.2-cp311-cp311-win_arm64.whl", hash = "sha256:20175a1c0f49863946ec20b7f63255768058ac4f07d2b9ded6a6b46cfb5a9100"},
{file = "mypy-1.20.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4dbfcf869f6b0517f70cf0030ba6ea1d6645e132337a7d5204a18d8d5636c02b"},
{file = "mypy-1.20.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b6481b228d072315b053210b01ac320e1be243dc17f9e5887ef167f23f5fae4"},
{file = "mypy-1.20.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:34397cdced6b90b836e38182076049fdb41424322e0b0728c946b0939ebdf9f6"},
{file = "mypy-1.20.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5da6976f20cae27059ea8d0c86e7cef3de720e04c4bb9ee18e3690fdb792066"},
{file = "mypy-1.20.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:56908d7e08318d39f85b1f0c6cfd47b0cac1a130da677630dac0de3e0623e102"},
{file = "mypy-1.20.2-cp312-cp312-win_amd64.whl", hash = "sha256:d52ad8d78522da1d308789df651ee5379088e77c76cb1994858d40a426b343b9"},
{file = "mypy-1.20.2-cp312-cp312-win_arm64.whl", hash = "sha256:785b08db19c9f214dc37d65f7c165d19a30fcecb48abfa30f31b01b5acaabb58"},
{file = "mypy-1.20.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:edfbfca868cdd6bd8d974a60f8a3682f5565d3f5c99b327640cedd24c4264026"},
{file = "mypy-1.20.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e2877a02380adfcdbc69071a0f74d6e9dbbf593c0dc9d174e1f223ffd5281943"},
{file = "mypy-1.20.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7488448de6007cd5177c6cea0517ac33b4c0f5ee9b5e9f2be51ce75511a85517"},
{file = "mypy-1.20.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bb9c2fa06887e21d6a3a868762acb82aec34e2c6fd0174064f27c93ede68ad15"},
{file = "mypy-1.20.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d56a78b646f2e3daa865bc70cd5ec5a46c50045801ca8ff17a0c43abc97e3ee"},
{file = "mypy-1.20.2-cp313-cp313-win_amd64.whl", hash = "sha256:2a4102b03bb7481d9a91a6da8d174740c9c8c4401024684b9ca3b7cc5e49852f"},
{file = "mypy-1.20.2-cp313-cp313-win_arm64.whl", hash = "sha256:a95a9248b0c6fd933a442c03c3b113c3b61320086b88e2c444676d3fd1ca3330"},
{file = "mypy-1.20.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:419413398fe250aae057fd2fe50166b61077083c9b82754c341cf4fd73038f30"},
{file = "mypy-1.20.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e73c07f23009962885c197ccb9b41356a30cc0e5a1d0c2ea8fd8fb1362d7f924"},
{file = "mypy-1.20.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c64e5973df366b747646fc98da921f9d6eba9716d57d1db94a83c026a08e0fb"},
{file = "mypy-1.20.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a65aa591af023864fd08a97da9974e919452cfe19cb146c8a5dc692626445dc"},
{file = "mypy-1.20.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4fef51b01e638974a6e69885687e9bd40c8d1e09a6cd291cca0619625cf1f558"},
{file = "mypy-1.20.2-cp314-cp314-win_amd64.whl", hash = "sha256:913485a03f1bcf5d279409a9d2b9ed565c151f61c09f29991e5faa14033da4c8"},
{file = "mypy-1.20.2-cp314-cp314-win_arm64.whl", hash = "sha256:c3bae4f855d965b5453784300c12ffc63a548304ac7f99e55d4dc7c898673aa3"},
{file = "mypy-1.20.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2de3dcea53babc1c3237a19002bc3d228ce1833278f093b8d619e06e7cc79609"},
{file = "mypy-1.20.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:52b176444e2e5054dfcbcb8c75b0b719865c96247b37407184bbfca5c353f2c2"},
{file = "mypy-1.20.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:688c3312e5dadb573a2c69c82af3a298d43ecf9e6d264e0f95df960b5f6ac19c"},
{file = "mypy-1.20.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29752dbbf8cc53f89f6ac096d363314333045c257c9c75cbd189ca2de0455744"},
{file = "mypy-1.20.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:803203d2b6ea644982c644895c2f78b28d0e208bba7b27d9b921e0ec5eb207c6"},
{file = "mypy-1.20.2-cp314-cp314t-win_amd64.whl", hash = "sha256:9bcb8aa397ff0093c824182fd76a935a9ba7ad097fcbef80ae89bf6c1731d8ec"},
{file = "mypy-1.20.2-cp314-cp314t-win_arm64.whl", hash = "sha256:e061b58443f1736f8a37c48978d7ab581636d6ab03e3d4f99e3fa90463bb9382"},
{file = "mypy-1.20.2-py3-none-any.whl", hash = "sha256:a94c5a76ab46c5e6257c7972b6c8cff0574201ca7dc05647e33e795d78680563"},
{file = "mypy-1.20.2.tar.gz", hash = "sha256:e8222c26daaafd9e8626dec58ae36029f82585890589576f769a650dd20fd665"},
]
[package.dependencies]
ast-serialize = ">=0.3.0,<1.0.0"
librt = {version = ">=0.10.0", markers = "platform_python_implementation != \"PyPy\""}
librt = {version = ">=0.8.0", markers = "platform_python_implementation != \"PyPy\""}
mypy_extensions = ">=1.0.0"
pathspec = ">=1.0.0"
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
@@ -2094,6 +2052,7 @@ dmypy = ["psutil (>=4.0)"]
faster-cache = ["orjson"]
install-types = ["pip"]
mypyc = ["setuptools (>=50)"]
native-parser = ["ast-serialize (>=0.1.1,<1.0.0)"]
reports = ["lxml"]
[[package]]
@@ -2900,151 +2859,121 @@ pytest = ">=7.4,<8.2.2 || >8.2.2"
[[package]]
name = "python-bidi"
version = "0.6.9"
version = "0.6.7"
description = "Python Bidi layout wrapping the Rust crate unicode-bidi"
optional = false
python-versions = ">=3.9"
python-versions = "*"
groups = ["main"]
files = [
{file = "python_bidi-0.6.9-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9796cadb2ae975876eaf69b5a7839ca72b820792af3fb393fe83ce34815b8e53"},
{file = "python_bidi-0.6.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b2809e78da1d01297f73c1bc1f4c174d8a5c53aed50395edc6b342bfb3516932"},
{file = "python_bidi-0.6.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:66763739ac2c3171347990494be5bfc1d7c790c05c6b74ead0a65e0393a2135c"},
{file = "python_bidi-0.6.9-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1e1a1cb947e6957dc5ebf664804a1111b764b72316aa797f8bbda103b78a6632"},
{file = "python_bidi-0.6.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:849308f7ccaade25e5625aaaa167cdbcd8dac3d6dbb418ce8772b970c3764e75"},
{file = "python_bidi-0.6.9-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:567b83da8b6baa409194d71106e7176c80294a5480430ebb06c9551ae1fa4413"},
{file = "python_bidi-0.6.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24be88621205d56299fe31b3842e1c166f6484fc91cb2d7da7ef08b24fa66e82"},
{file = "python_bidi-0.6.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:52985596d3d59dcc2c0103ca9a54fe5a6fc7b89c336c2359ba46824ad316977b"},
{file = "python_bidi-0.6.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1ce0cac230c021e097dd9a65274410b59a2b6f11007eaa5cbb082496d4dfeb61"},
{file = "python_bidi-0.6.9-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:cc4f17e0202e2e53be8a0f06855e970478187014e619c0ad1ccf5708f79c335b"},
{file = "python_bidi-0.6.9-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e9fc331a37778fc55e5526cf66a0e76e241c4f3df6cd54c76358d777c87c9e64"},
{file = "python_bidi-0.6.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ff713d6cb9d2a5c5ccee279521cc3d3105cf92d23317fd65380003afc097fc8e"},
{file = "python_bidi-0.6.9-cp310-cp310-win32.whl", hash = "sha256:7517512bdd8e7cf71c80ad3a2970356d035bf1224a795c5b91d6a6fbe20d9d3e"},
{file = "python_bidi-0.6.9-cp310-cp310-win_amd64.whl", hash = "sha256:3b7155bf8e904352ce47585d09a0d203fadcc052f3356d88fc9be8ff1ef763b1"},
{file = "python_bidi-0.6.9-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:7a205fbb2e540d8faa028da754ddba1d1132fe6fe9d18e6d14b090b80c564322"},
{file = "python_bidi-0.6.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c3554a3cb9b95e503bc348a8c461b330bf1812c500f51bc9bbd789544fc4a8aa"},
{file = "python_bidi-0.6.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:423d6b3d2cc1a58ae320faa7b6bb25604757f749992b6013fb856fa8721213a3"},
{file = "python_bidi-0.6.9-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e3c1e19e1fc93ff55979f33f1cea66e431dd324577b2ed64031b217cb3986065"},
{file = "python_bidi-0.6.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb235da41849ac506311585f884600e65978163d4efd005a97cb2647911ee645"},
{file = "python_bidi-0.6.9-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6636db8ddb50f80f6ea6cae546be09b14a56a9e752c672ca2c1c1bccd90866ff"},
{file = "python_bidi-0.6.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29e40e02bf2bddec225730eb3ba9f4ca948dced9877358458b74d7a447d936ac"},
{file = "python_bidi-0.6.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ab9f3e66abc98480908fde40e1cdea1416f05ff987ef925e701f00de92949e1e"},
{file = "python_bidi-0.6.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cfa935eddc9158e5fbf4aa2b545cdaf362b7248e25d464619fbbd3a4a20d6640"},
{file = "python_bidi-0.6.9-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:4247406841a4a20f0efc2a70a3b6ecd74c9668688b11197b0f0b936d5b22026e"},
{file = "python_bidi-0.6.9-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a2b1d14c49f723f608e86449c8a029828992719187870c12b4378a2f490d0581"},
{file = "python_bidi-0.6.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:48380a12a57ac64803387ae47cf1c4e77d6c2bffdd2c15f402aee21ec14c4e6c"},
{file = "python_bidi-0.6.9-cp311-cp311-win32.whl", hash = "sha256:713cfd6aef1ad451f80be9d53603e23ebdb64a9d3611add740a782767ed37902"},
{file = "python_bidi-0.6.9-cp311-cp311-win_amd64.whl", hash = "sha256:be28092eef062fad59e7e0402bf2c1db797ccadedd866a53bbd7ddb97e42cac1"},
{file = "python_bidi-0.6.9-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ec7b9b08ab8582c6e582f9be312c6ec53843ae1026cceda8257e83e14f92d015"},
{file = "python_bidi-0.6.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c59f8e5acd3aa1b0f5cc3aba68243c9501eb555d26c898fb11dac6890d63f5ac"},
{file = "python_bidi-0.6.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:399fca7e980b22a34cd4bfab011f9af64dbec3f150b82030aa77f5e132473bf6"},
{file = "python_bidi-0.6.9-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd6986c606a3ff843a0307d2283c50a6150d374bddba5989f76e012ef1024253"},
{file = "python_bidi-0.6.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c1a9d0b3b7b34f22e56cb3b8c0a8c985c3ebcb257f26d05c3c901cd2893ce68b"},
{file = "python_bidi-0.6.9-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8883917d0a402fc08317ce36b3ece81640d6623a29356d27f0e2a61165c8793b"},
{file = "python_bidi-0.6.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3f13ef8a8af11ec549fc5d6cc7adf580794f052e5a53499d9104246005effe9"},
{file = "python_bidi-0.6.9-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:788080701d2c326a202d251238d8dd6b0993e86ab4f230764a9500ddd536d08c"},
{file = "python_bidi-0.6.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e35aad6b388bcd0161e57a0b6762f5f925519b57fc0a7a06c205581ce33b901d"},
{file = "python_bidi-0.6.9-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:960b4be36090809c377fdcf863202e6313d23ff424d4be3eac4c1ffcda565ff4"},
{file = "python_bidi-0.6.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4bb0c2965f64497c3dfeb550dac82ffd814aeeec19f69214438c40f41bcd517f"},
{file = "python_bidi-0.6.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f7a22759b6403984b6fb31e5f43f02b952b2093b669b475b214ff2ce7d8473f0"},
{file = "python_bidi-0.6.9-cp312-cp312-win32.whl", hash = "sha256:663e49dc49323e82b76009c60d33dd89ffa13920abd33f17e9de62d85b63af07"},
{file = "python_bidi-0.6.9-cp312-cp312-win_amd64.whl", hash = "sha256:66a4a9c7b8e92dd29c9e8abbc2e4e8632859eb20ec67778a1e212a9fa44bf3ff"},
{file = "python_bidi-0.6.9-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f336f47260cdd6212e2b94b586675f0d07bbd0306bb1b0e7fee125ea2426dfd5"},
{file = "python_bidi-0.6.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c3e4445f0fd1e2c16d3efdb28622a38f57ab4b971eb47803a334be477cee173e"},
{file = "python_bidi-0.6.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d49f47f79828f7d9d395fb999da04e178fe40fa5b7ae1937c25564280b5e8db"},
{file = "python_bidi-0.6.9-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2c9621c8da0a2ab3c77b3099a52e3a8011728de05b07ded9b8a2b6f38610c183"},
{file = "python_bidi-0.6.9-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c1c38c3aa509ace1d24a731a0c30049271865e460be7d53fae35254dc8bbe644"},
{file = "python_bidi-0.6.9-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f21a1ad97d28d1e4fcc456a4394983d6f3b0c7f475c6adfcc9d0b7ca627c5b6"},
{file = "python_bidi-0.6.9-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6cf6941062f0589291a396b3d81636b7e35a99105098bc147c48e077c0fb4b45"},
{file = "python_bidi-0.6.9-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6fe65275c327cbeb3d2d4c397244dfe17db3393847acfbdd0d4627b35d69fd00"},
{file = "python_bidi-0.6.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9eb549ad22a310e2bc01c6a65237d56bed74c0e63379284195e5a92fb5ec7dbc"},
{file = "python_bidi-0.6.9-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:156ea1fd59f8bf4bc6a2b8c02af0d9fca2ddffd6a62397e13c1c31733f1d0714"},
{file = "python_bidi-0.6.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5776be58d35617ea68616aa074bb480d320a41424265e1c2e656a3730d3316b7"},
{file = "python_bidi-0.6.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a23442854115146d29dace54aaa755b50e3db477f716c2fd0091081b17ae9097"},
{file = "python_bidi-0.6.9-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:b12451efb895dd1ca4e5a106fb757185027cd8a73d799318699c2d48a60257b3"},
{file = "python_bidi-0.6.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c947040fde79d3d0e0f3232207ddbd8b5fd708e58a92aaf30368c4cf0578a8a9"},
{file = "python_bidi-0.6.9-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f64f4c2f66195c22a69d104831680963eb306e41417975dca4ead70e4640343d"},
{file = "python_bidi-0.6.9-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:355bbdb72a9ec8b1b2db8c935d674104f1d0c5897ab9957b45ff7425b18e08b8"},
{file = "python_bidi-0.6.9-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:18f1a7ec71e19fd7162b0a722ba6230f61a87215069e093cf985d175cbc2b60f"},
{file = "python_bidi-0.6.9-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc1f7405dda0fb7949b9d6319c589ff6bcda1a92125783e56079058c8c29293a"},
{file = "python_bidi-0.6.9-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0701081afd8fb1bea8acf7f8cfb44ef4a25b2d3116c8c3f67abebf9d31930196"},
{file = "python_bidi-0.6.9-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:474aa2633125c003334e4d785e5a00bd296503279cd3497415544656bde00b48"},
{file = "python_bidi-0.6.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:bb0517d9668d7c85dac2d815d597e8861bc8c0fbb1ed6e26e8b5e697f5a4e2ad"},
{file = "python_bidi-0.6.9-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:a7b6f1d46e093025ba6b9025723086101458a5d0832144389eee2c94c1af0007"},
{file = "python_bidi-0.6.9-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:557b657a82c6e2a7abb8b1e27d9464fb6a45554d6a25ea7a745ab8f9052c7e9c"},
{file = "python_bidi-0.6.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:58ba120a32ca5271169ccd9bf012ccb5a66df228c23baa1bdf1f587fceb1d7dc"},
{file = "python_bidi-0.6.9-cp313-cp313t-win32.whl", hash = "sha256:f150b022f138cade97584fd7c4f00ef17a472e862a6f0a01a571f64a020838ed"},
{file = "python_bidi-0.6.9-cp313-cp313t-win_amd64.whl", hash = "sha256:c3d0694a868818b294812bfc0c633f25bb8284736254c9a58db61f65944e83b8"},
{file = "python_bidi-0.6.9-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:dc0d9666b40af9c4c9b49a5f363c9920f925e9d37adb5adcc0ceacafa2ef706c"},
{file = "python_bidi-0.6.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8210f937e7b5e1486c6397be8aea34013752c976a1b7333770cb3b3321dd2bf7"},
{file = "python_bidi-0.6.9-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:884dcad2f340a1332af32057cf14f4004b4cae4345eaf7e53de87f918a75cdff"},
{file = "python_bidi-0.6.9-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4db41d57a202bab30663a04bc32ab9226e2d92977bd158f4ca9622a77c3d1aa7"},
{file = "python_bidi-0.6.9-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8572215e4df139ce60e7b405a7986e9fff2144359d3f55d203d602e17a24e00a"},
{file = "python_bidi-0.6.9-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:353ecae71b10a9771ae530ed43f7476ab99c45a12f5f6c63517edbf4dbe17a46"},
{file = "python_bidi-0.6.9-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43f8cb29527a890a8e4bbe537c406ee31b83a4ea4198d431ffb9c1b15d57309b"},
{file = "python_bidi-0.6.9-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e8631204986f083782e558ad0adb4684b4070307b0dd6e0dd5631586378755ed"},
{file = "python_bidi-0.6.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:92faaceece4d33e5db69439dd11d9d285c41cbfb2eec743ae600eb9afd065687"},
{file = "python_bidi-0.6.9-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:73543aa758044f7eee4ca83cb691a298529d06099cf6563f107a49f0ff5b2816"},
{file = "python_bidi-0.6.9-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:2e7f1c83737642a8cbb3c3ca3821a56251737b38be4f016dfda74627418daf45"},
{file = "python_bidi-0.6.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:da2825d2d8aa9246c6c7295bac578b45c3bcc3ca7f3c7c6be24bb38624685887"},
{file = "python_bidi-0.6.9-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:a78dc5e4e46a7df8d3056d56f08a11b165554b534d0b6a9e9bd80de17806e9e1"},
{file = "python_bidi-0.6.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:315d86b724a60a077b076b74a4d934b1565578c4b6335594b40b3fde2082721f"},
{file = "python_bidi-0.6.9-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c885b7aa82074e13e5b648a81d157a88256b9c10c8018c304b52b3db46a967e"},
{file = "python_bidi-0.6.9-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0110b79fd81f6b8f051f468070995713303c177368319b4879d29a32ee67b977"},
{file = "python_bidi-0.6.9-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0c85f8af6069acf9305cbb057c98536151de82bf720b551f9a89d804fd13505"},
{file = "python_bidi-0.6.9-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e834a97f190c65d692cab4c121a70672840a101cdad6e09ccde21c10836895d"},
{file = "python_bidi-0.6.9-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de2b2eef20f3c4fd925f3ab9f59dd2d8d3b94dbd544f989d93978cf723bc1711"},
{file = "python_bidi-0.6.9-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:09261ca97597e40cc3dd042bae4a00371f06200d4afb5802ad2e6e63f0a493e7"},
{file = "python_bidi-0.6.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4d57f4a81cf375c3922f1e12ca968f7519d609879b085273c0afc9e4ab6ed215"},
{file = "python_bidi-0.6.9-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:4eaeb4122a8cee7012ce7b1daeb49b685b8ac5870d961fa9116db67a7ade3ad1"},
{file = "python_bidi-0.6.9-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:244c2cf6d0cc8d1614e793d130f7e205d259a13e04be93a9e50c48d3a8ccae98"},
{file = "python_bidi-0.6.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f7f76dbed88f395a85822a057ca29c0c29d4eeea6348d33d578d60fc257770cf"},
{file = "python_bidi-0.6.9-cp314-cp314t-win32.whl", hash = "sha256:f6b56761239738ce81222d7b50d7331d4195222089b2f2406375ad99bc60e9bb"},
{file = "python_bidi-0.6.9-cp314-cp314t-win_amd64.whl", hash = "sha256:9c0baf4c4bc7052006b6d5db976997112cb4a66b417928a6bfd2084638efa0ea"},
{file = "python_bidi-0.6.9-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:0400b820867eb9ee79ad2a0c46fcbbca0bd68f806ee49882b188fca5f1bd9fb1"},
{file = "python_bidi-0.6.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6ebc0021b3d4f652f049f655e0704bf5f03a4ac56b968edab439076bd2b4c881"},
{file = "python_bidi-0.6.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d81b652ee7b5cf2573f65033a1cdce205a791786a6638c50becec6020b2af8d"},
{file = "python_bidi-0.6.9-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:96fa44e578e9ebff5a713ea0c360a87e857fc3f192050a89ec79eb63440d0fe7"},
{file = "python_bidi-0.6.9-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9004d122ce3a2d478937c30a8a32b3d6dada67fde52b19aced1300666997ebbd"},
{file = "python_bidi-0.6.9-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:baa28d2e35719eeedadc0c3523f9c72c286534b2495807a7c0b28b7db959017b"},
{file = "python_bidi-0.6.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f523edb20b15d660d27d3c0017a2170202872cdf9161b26d6531941d1df1202a"},
{file = "python_bidi-0.6.9-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bed73162d24c8cd749ddfd53b42f1af199ed80462db9314db23042103a92c9e7"},
{file = "python_bidi-0.6.9-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:65ed44873fe9109df8ecc17d9b483fb54800ff3c77b0f9e97b3b122426c607f3"},
{file = "python_bidi-0.6.9-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:525b2e0eea019f7f74122f2ee2d703a48e7ef20c85a129a964d2316f20158a2d"},
{file = "python_bidi-0.6.9-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:71a084541519c7c36774c1cb37fa71c641005a18b990a4d84fccaac8ed5f70c5"},
{file = "python_bidi-0.6.9-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c63906859241ee962a703515a64d0d60f322f3ea52645fd985c08e77f8acd1d5"},
{file = "python_bidi-0.6.9-cp38-cp38-win32.whl", hash = "sha256:cd78c22fee35e9a002150308d4267ce0e54bb0ad7461075519c30efc320a7391"},
{file = "python_bidi-0.6.9-cp38-cp38-win_amd64.whl", hash = "sha256:568c398e23516c9e875274b3f97cec3caaf1a9ac587c42e660f3a4bcc2ef9ea1"},
{file = "python_bidi-0.6.9-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:0f7f2e0897bfbc6cd7990e1bba698958d177d8a0c57aae0e79dffc615f0b0e74"},
{file = "python_bidi-0.6.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c1dd21dbc8e3eb3a3740ca95e24edbd03cf61fa39d50d0dddc7760dfc81b7077"},
{file = "python_bidi-0.6.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61615404a8229862564d4718fb9a2e923faf1943260c81f173e2833cf1d6975b"},
{file = "python_bidi-0.6.9-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ae38ff978752444875f0f2fe5d3fc0e55905256c3279d893238887819e38819a"},
{file = "python_bidi-0.6.9-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c0ac30d86eef13eeaadd9852df302ec6d869606741cefd11c5738a75bd067a3"},
{file = "python_bidi-0.6.9-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:44f6d4f960088dc43dd4acbfd40e1397d91dca41236a34505832fb7eaa877d16"},
{file = "python_bidi-0.6.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94cc0a904a0df3fc9f65624d8bc9464a893ae03754c0f067ab7d8436d915619b"},
{file = "python_bidi-0.6.9-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:58f71274eb4984e509eff345a23c092aad846903d10dd1be26cd664ac7d9ecdb"},
{file = "python_bidi-0.6.9-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9d494d08bbba4a2ae8101dc9b136569375e9520335875976250985b222861577"},
{file = "python_bidi-0.6.9-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:d35307e52e367d22dc8d07e944d75b56d48a3a46b6da6b34c4591fbf7d16e57b"},
{file = "python_bidi-0.6.9-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:886535a1553afe84da59be618493e49f7ac062fd90787ac23fa02adc2f350faf"},
{file = "python_bidi-0.6.9-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9418e940adeb28c2088acee3ec7b8843281afb58af675cbe6952769d97ce0ef5"},
{file = "python_bidi-0.6.9-cp39-cp39-win32.whl", hash = "sha256:63cb3eec71b62fa35b4268e073f20fd44ce9b3e0d545b0c40e70f595562e9aa3"},
{file = "python_bidi-0.6.9-cp39-cp39-win_amd64.whl", hash = "sha256:50d1def0effcbb7d9d4798b64147a95cf8b1d3c5437513dd78d64fe26f60128e"},
{file = "python_bidi-0.6.9-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:769b0f60a2b18e4aec6a38a911cba61c5bdbdf99bb0e747a41054f0963d689ef"},
{file = "python_bidi-0.6.9-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:eaca4f3dc857b36217980cde0619a41dbd0f96a8a18b8a4e26212359b5ab3b62"},
{file = "python_bidi-0.6.9-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b89925bddd7db940b255f3df14fa5a99ba9660a64b5ae6ef2f31fd2be4a4113a"},
{file = "python_bidi-0.6.9-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3593ec003096ed3fc4e6aacd09f624a9bbc87e0743b037d45c70747fbd33e10a"},
{file = "python_bidi-0.6.9-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb7809f3f5e7994408368fa8a121873cab9d6477facbddec95bf81d86d767fe"},
{file = "python_bidi-0.6.9-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e1b7a5d97d6908915b866e3d4a3a0e1a6753e06d2cb60ff1fab1996c6f11321"},
{file = "python_bidi-0.6.9-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee7854584d13b48e38ea05aea967eaaf03ad9dc8f3534e95660b5aea4978fb91"},
{file = "python_bidi-0.6.9-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b83ee347d307dcb7a269fffaa55c416fb0596ba97079c768f4c7cfd87c8552cb"},
{file = "python_bidi-0.6.9-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:e496a4b5cefed61f13483022259e27a30a51361f1e3ae7b511841738e2b131c9"},
{file = "python_bidi-0.6.9-pp311-pypy311_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:1849cec20846043c3e51824425313abe74e65b68528984b3985f007c176c1027"},
{file = "python_bidi-0.6.9-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:7b8bb2435ec965daeff74a6f4c4808467586a6a2330933922be5ff2408816560"},
{file = "python_bidi-0.6.9-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:25fb39bdd9f9ed47ca023df83f3ccf089556aa83421ec568f2ead92edbe08b48"},
{file = "python_bidi-0.6.9.tar.gz", hash = "sha256:001c1769893fd859216b0dc39dd4679c7260bf292c3637b727a928402a254322"},
{file = "python_bidi-0.6.7-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:94dbfd6a6ec0ae64b5262290bf014d6063f9ac8688bda9ec668dc175378d2c80"},
{file = "python_bidi-0.6.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d8274ff02d447cca026ba00f56070ba15f95e184b2d028ee0e4b6c9813d2aaf9"},
{file = "python_bidi-0.6.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24afff65c581a5d6f658a9ec027d6719d19a1d8a4401000fdb22d2eeb677b8e3"},
{file = "python_bidi-0.6.7-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8678c2272e7bd60a75f781409e900c9ddb9f01f55c625d83ae0d49dfc6a2674f"},
{file = "python_bidi-0.6.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4cd82e65b5aeb31bd73534e61ece1cab625f4bcbdc13bc4ddc5f8cbfb37c24a"},
{file = "python_bidi-0.6.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dde1c3f3edb1f0095dcbf79cf8a0bb768f9539e809d0ad010d78200eea97d42a"},
{file = "python_bidi-0.6.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c463ae15e94b1c6a8a50bd671d6166b0b0d779fd1e56cbf46d8a4a84c9aa2d0"},
{file = "python_bidi-0.6.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f9fa1257e075eeeed67d21f95e411036b7ca2b5c78f757d4ac66485c191720a"},
{file = "python_bidi-0.6.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9adeec7cab0f2c2c291bd7faf9fa3fa233365fd0bf1c1c27a6ddd6cc563d4b32"},
{file = "python_bidi-0.6.7-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:3b96744e4709f4445788a3645cea7ef8d7520ccd4fa8bbbfb3b650702e12c1e6"},
{file = "python_bidi-0.6.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:8860d67dc04dc530b8b4f588f38b7341a76f2ec44a45685a2d54e9dcffa5d15a"},
{file = "python_bidi-0.6.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a4319f478ab1b90bbbe9921606ecb7baa0ebf0b332e821d41c3abdf1a30f0c35"},
{file = "python_bidi-0.6.7-cp310-cp310-win32.whl", hash = "sha256:8d4e621caadfdbc73d36eabdb2f392da850d28c58b020738411d09dda6208509"},
{file = "python_bidi-0.6.7-cp310-cp310-win_amd64.whl", hash = "sha256:fd87d112eda1f0528074e1f7c0312881816cb75854133021124269a27c6c48dc"},
{file = "python_bidi-0.6.7-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a8892a7da0f617135fe9c92dc7070d13a0f96ab3081f9db7ff5b172a3905bd78"},
{file = "python_bidi-0.6.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:06650a164e63e94dc8a291cc9d415b4027cb1cce125bc9b02dac0f34d535ed47"},
{file = "python_bidi-0.6.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6df7be07af867ec1d121c92ea827efad4d77b25457c06eeab477b601e82b2340"},
{file = "python_bidi-0.6.7-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:73a88dc333efc42281bd800d5182c8625c6e11d109fc183fe3d7a11d48ab1150"},
{file = "python_bidi-0.6.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f24189dc3aea3a0a94391a047076e1014306b39ba17d7a38ebab510553cd1a97"},
{file = "python_bidi-0.6.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a507fe6928a27a308e04ebf2065719b7850d1bf9ff1924f4e601ef77758812bd"},
{file = "python_bidi-0.6.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fbbffb948a32f9783d1a28bc0c53616f0a76736ed1e7c1d62e3e99a8dfaab869"},
{file = "python_bidi-0.6.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f7e507e1e798ebca77ddc9774fd405107833315ad802cfdaa1ab07b6d9154fc8"},
{file = "python_bidi-0.6.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:849a57d39feaf897955d0b19bbf4796bea53d1bcdf83b82e0a7b059167eb2049"},
{file = "python_bidi-0.6.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5ebc19f24e65a1f5c472e26d88e78b9d316e293bc6f205f32de4c4e99276336e"},
{file = "python_bidi-0.6.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:24388c77cb00b8aa0f9c84beb7e3e523a3dac4f786ece64a1d8175a07b24da72"},
{file = "python_bidi-0.6.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:19737d217088ef27014f98eac1827c5913e6fb1dea96332ed84ede61791070d9"},
{file = "python_bidi-0.6.7-cp311-cp311-win32.whl", hash = "sha256:95c9de7ebc55ffb777548f2ecaf4b96b0fa0c92f42bf4d897b9f4cd164ec7394"},
{file = "python_bidi-0.6.7-cp311-cp311-win_amd64.whl", hash = "sha256:898db0ea3e4aaa95b7fecba02a7560dfbf368f9d85053f2875f6d610c4d4ec2c"},
{file = "python_bidi-0.6.7-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:11c51579e01f768446a7e13a0059fea1530936a707abcbeaad9467a55cb16073"},
{file = "python_bidi-0.6.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47deaada8949af3a790f2cd73b613f9bfa153b4c9450f91c44a60c3109a81f73"},
{file = "python_bidi-0.6.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b38ddfab41d10e780edb431edc30aec89bee4ce43d718e3896e99f33dae5c1d3"},
{file = "python_bidi-0.6.7-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2a93b0394cc684d64356b0475858c116f1e335ffbaba388db93bf47307deadfa"},
{file = "python_bidi-0.6.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ec1694134961b71ac05241ac989b49ccf08e232b5834d5fc46f8a7c3bb1c13a9"},
{file = "python_bidi-0.6.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8047c33b85f7790474a1f488bef95689f049976a4e1c6f213a8d075d180a93e4"},
{file = "python_bidi-0.6.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d9de35eb5987da27dd81e371c52142dd8e924bd61c1006003071ea05a735587"},
{file = "python_bidi-0.6.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a99d898ad1a399d9c8cab5561b3667fd24f4385820ac90c3340aa637aa5adfc9"},
{file = "python_bidi-0.6.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5debaab33562fdfc79ffdbd8d9c51cf07b8529de0e889d8cd145d78137aab21e"},
{file = "python_bidi-0.6.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c11c62a3cdb9d1426b1536de9e3446cb09c7d025bd4df125275cae221f214899"},
{file = "python_bidi-0.6.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6c051f2d28ca542092d01da8b5fe110fb6191ff58d298a54a93dc183bece63bf"},
{file = "python_bidi-0.6.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:95867a07c5dee0ea2340fe1d0e4f6d9f5c5687d473193b6ee6f86fa44aac45d1"},
{file = "python_bidi-0.6.7-cp312-cp312-win32.whl", hash = "sha256:4c73cd980d45bb967799c7f0fc98ea93ae3d65b21ef2ba6abef6a057720bf483"},
{file = "python_bidi-0.6.7-cp312-cp312-win_amd64.whl", hash = "sha256:d524a4ba765bae9b950706472a77a887a525ed21144fe4b41f6190f6e57caa2c"},
{file = "python_bidi-0.6.7-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1c061207212cd1db27bf6140b96dcd0536246f1e13e99bb5d03f4632f8e2ad7f"},
{file = "python_bidi-0.6.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a2eb8fca918c7381531035c3aae31c29a1c1300ab8a63cad1ec3a71331096c78"},
{file = "python_bidi-0.6.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:414004fe9cba33d288ff4a04e1c9afe6a737f440595d01b5bbed00d750296bbd"},
{file = "python_bidi-0.6.7-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5013ba963e9da606c4c03958cc737ebd5f8b9b8404bd71ab0d580048c746f875"},
{file = "python_bidi-0.6.7-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad5f0847da00687f52d2b81828e8d887bdea9eb8686a9841024ea7a0e153028e"},
{file = "python_bidi-0.6.7-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:26a8fe0d532b966708fc5f8aea0602107fde4745a8a5ae961edd3cf02e807d07"},
{file = "python_bidi-0.6.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6323e943c7672b271ad9575a2232508f17e87e81a78d7d10d6e93040e210eddf"},
{file = "python_bidi-0.6.7-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:349b89c3110bd25aa56d79418239ca4785d4bcc7a596e63bb996a9696fc6a907"},
{file = "python_bidi-0.6.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e7cad66317f12f0fd755fe41ee7c6b06531d2189a9048a8f37addb5109f7e3e3"},
{file = "python_bidi-0.6.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:49639743f1230648fd4fb47547f8a48ada9c5ca1426b17ac08e3be607c65394c"},
{file = "python_bidi-0.6.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4636d572b357ab9f313c5340915c1cf51e3e54dd069351e02b6b76577fd1a854"},
{file = "python_bidi-0.6.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d7310312a68fdb1a8249cf114acb5435aa6b6a958b15810f053c1df5f98476e4"},
{file = "python_bidi-0.6.7-cp313-cp313-win32.whl", hash = "sha256:ec985386bc3cd54155f2ef0434fccbfd743617ed6fc1a84dae2ab1de6062e0c6"},
{file = "python_bidi-0.6.7-cp313-cp313-win_amd64.whl", hash = "sha256:f57726b5a90d818625e6996f5116971b7a4ceb888832337d0e2cf43d1c362a90"},
{file = "python_bidi-0.6.7-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:b0bee27fb596a0f518369c275a965d0448c39a0730e53a030b311bb10562d4d5"},
{file = "python_bidi-0.6.7-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6c19ab378fefb1f09623f583fcfa12ed42369a998ddfbd39c40908397243c56b"},
{file = "python_bidi-0.6.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:630cee960ba9e3016f95a8e6f725a621ddeff6fd287839f5693ccfab3f3a9b5c"},
{file = "python_bidi-0.6.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:0dbb4bbae212cca5bcf6e522fe8f572aff7d62544557734c2f810ded844d9eea"},
{file = "python_bidi-0.6.7-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:1dd0a5ec0d8710905cebb4c9e5018aa8464395a33cb32a3a6c2a951bf1984fe5"},
{file = "python_bidi-0.6.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4ea928c31c7364098f853f122868f6f2155d6840661f7ea8b2ccfdf6084eb9f4"},
{file = "python_bidi-0.6.7-cp314-cp314-win32.whl", hash = "sha256:f7c055a50d068b3a924bd33a327646346839f55bcb762a26ec3fde8ea5d40564"},
{file = "python_bidi-0.6.7-cp314-cp314-win_amd64.whl", hash = "sha256:8a17631e3e691eec4ae6a370f7b035cf0a5767f4457bd615d11728c23df72e43"},
{file = "python_bidi-0.6.7-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:0f86e447e94ae78db7d56e7da2124c435eaee4425c87d3d92aea271317811112"},
{file = "python_bidi-0.6.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4283f8b517411cc81b3c92d11998981fe54ac0d2300f4c58d803e0c071aba1ba"},
{file = "python_bidi-0.6.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01ff2fd676ef8351f32e820b2d3b61eac875a21702d2118263a2641b458e1996"},
{file = "python_bidi-0.6.7-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f7e5072269c34a1b719910ee4decf13b288159fb320f18aba3885f6b6aab7753"},
{file = "python_bidi-0.6.7-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:257d6dd0e07221f1dc8720fa61158471f5aae30d5f89837c38a026386151c250"},
{file = "python_bidi-0.6.7-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1395e236c71f11267860b53293a33b19b991b06e0f4ac61045b892e6a99d96f2"},
{file = "python_bidi-0.6.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be1bdbd52145dfe46880d8bb56eacc25aa75c3bb075fa103de7974295eb2811f"},
{file = "python_bidi-0.6.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7336a3c4ba4fc9e6741fbe60c6483266fe39e1f24830724dfce453471d11fa40"},
{file = "python_bidi-0.6.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:1ba28642928d1c8fdb18b0632fe931f156e888c646326a3ad8eb3e55ee904951"},
{file = "python_bidi-0.6.7-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:80e6fd06f6e4074d183cea73962c89cf76cb4f70c0ee403689f57a429ebde488"},
{file = "python_bidi-0.6.7-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ef9d103706560c15fecaf7d3cff939e0f68ce5763cf0e64d0e4e5d37f9bdd2d1"},
{file = "python_bidi-0.6.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:3a85275dfc24a96629da058c4c2fc93af6390aefe2f7cdde1500b6ac3fd40ca0"},
{file = "python_bidi-0.6.7-cp38-cp38-win32.whl", hash = "sha256:c3d93171dd65b36eca5367acf19eef82c79b4df557cb4bd0daf323b7a27f2d3b"},
{file = "python_bidi-0.6.7-cp38-cp38-win_amd64.whl", hash = "sha256:d879be7fb5296409e18731c7ba666d56ecd45b816b2c9eb35138aa1d7777aeb5"},
{file = "python_bidi-0.6.7-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ff06e4aa781aa4f68fbfaf1e727fe221fa1c552fef8ae70b6d2a0178e1f229ad"},
{file = "python_bidi-0.6.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:caa71c723f512f8d859fa239573086e16f38ffc426b5b2f7dab5d40fdb356c80"},
{file = "python_bidi-0.6.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77fea54c2379b93def4ed16db6390e1232e7b235679587295a23dd8b1925475f"},
{file = "python_bidi-0.6.7-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df5e9db9539d70426f5d20c7ebb6f7b33da5fbd40620e11261fe3fba7e177145"},
{file = "python_bidi-0.6.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b31d66b62736b8514982a24a7dedcf8c062b27a8e9b51e52d7a5899045a45fe1"},
{file = "python_bidi-0.6.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8814db38fa317bebec8eb74b826bae7d0cb978a7eca30dfe4ecf60e61f06ee0b"},
{file = "python_bidi-0.6.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab806fd026bfd48bade5e21e06d0d799cbfad32f236989ff6f37db03a5fbe34f"},
{file = "python_bidi-0.6.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8a18c61817f3210ba74ad5792c8a5048d9550ba233233a0a8fe35800350988f4"},
{file = "python_bidi-0.6.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0cb75e8a410166fd677d55095e505bf6a4773c066f51efbda72d302ebc56e79b"},
{file = "python_bidi-0.6.7-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:aa4136f8ccb9a8cd32befd1b3882c2597e6791e64e8b3cf3129c55549b5de62f"},
{file = "python_bidi-0.6.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4d84e70923392f8c9611f0fb6b341577346ef6224f3809b05f0ae1fbf8f17578"},
{file = "python_bidi-0.6.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:24a4a268289bbe80ad7da3064d7325f1571173859e8ad75d2f99075d5278b02b"},
{file = "python_bidi-0.6.7-cp39-cp39-win32.whl", hash = "sha256:ab2a5177522b62426db897b655a02f574e27d9735bbeb6da41bc981b771df636"},
{file = "python_bidi-0.6.7-cp39-cp39-win_amd64.whl", hash = "sha256:ce86d9dfc6b409ad16556384244572bb3cbefa2ca0f0eab7fba0ff2112b2f068"},
{file = "python_bidi-0.6.7-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c9a679b24f5c6f366a0dec75745e1abeae2f597f033d0d54c74cbe62e7e6ae28"},
{file = "python_bidi-0.6.7-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:05fe5971110013610f0db40505d0b204edc756e92eafac1372a464f8b9162b11"},
{file = "python_bidi-0.6.7-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17572944e6d8fb616d111fc702c759da2bf7cedab85a3e4fa2af0c9eb95ed438"},
{file = "python_bidi-0.6.7-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3b63d19f3f56ff7f99bce5ca9ef8c811dbf0f509d8e84c1bc06105ed26a49528"},
{file = "python_bidi-0.6.7-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1350033431d75be749273236dcfc808e54404cd6ece6204cdb1bc4ccc163455"},
{file = "python_bidi-0.6.7-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c5fb99f774748de283fadf915106f130b74be1bade934b7f73a7a8488b95da1"},
{file = "python_bidi-0.6.7-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d28e2bdcadf5b6161bb4ee9313ce41eac746ba57e744168bf723a415a11af05"},
{file = "python_bidi-0.6.7-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c3777ae3e088e94df854fbcbd8d59f9239b74aac036cb6bbd19f8035c8e42478"},
{file = "python_bidi-0.6.7-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:77bb4cbadf4121db395189065c58c9dd5d1950257cc1983004e6df4a3e2f97ad"},
{file = "python_bidi-0.6.7-pp311-pypy311_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:f1fe71c203f66bc169a393964d5702f9251cfd4d70279cb6453fdd42bd2e675f"},
{file = "python_bidi-0.6.7-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:d87ed09e5c9b6d2648e8856a4e556147b9d3cd4d63905fa664dd6706bc414256"},
{file = "python_bidi-0.6.7-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:766d5f5a686eb99b53168a7bdfb338035931a609bdbbcb537cef9e050a86f359"},
{file = "python_bidi-0.6.7.tar.gz", hash = "sha256:c10065081c0e137975de5d9ba2ff2306286dbf5e0c586d4d5aec87c856239b41"},
]
[package.extras]
dev = ["nox", "pytest"]
dev = ["pytest"]
[[package]]
name = "python-dateutil"
@@ -3865,4 +3794,4 @@ propcache = ">=0.2.1"
[metadata]
lock-version = "2.1"
python-versions = "^3.10"
content-hash = "7a178b95a83821789c83e5b693cc4701a1b99988e7147504ff2b7b34b8065d9b"
content-hash = "d3f6266169a91737f143c90f423e079f60ac2ef3aef388de9ef0a481ab7e7ee3"
+1 -1
View File
@@ -92,7 +92,7 @@ pytest-cov = ">=6,<8"
pytest-httpserver = "^1.0.0"
pytest-rerunfailures = ">=15.1,<17.0"
reportlab = "^4.4.3"
mypy = ">=1.14.1,<3.0.0"
mypy = "^1.14.1"
tuna = "^0.5.11"
coverage = "^7.9.2"
black = ">=25.1,<27.0"
+70 -67
View File
@@ -1,10 +1,11 @@
## List of supported sites (search methods): total 3154
## List of supported sites (search methods): total 3157
Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://www.facebook.com/) [Facebook (https://www.facebook.com/)](https://www.facebook.com/)*: top 2, social*
1. ![](https://www.google.com/s2/favicons?domain=https://www.youtube.com/) [YouTube (https://www.youtube.com/)](https://www.youtube.com/)*: top 3, video*
1. ![](https://www.google.com/s2/favicons?domain=https://www.youtube.com/) [YouTube User (https://www.youtube.com/)](https://www.youtube.com/)*: top 3, video*
1. ![](https://www.google.com/s2/favicons?domain=https://www.instagram.com/) [Instagram (https://www.instagram.com/)](https://www.instagram.com/)*: top 4, photo, social*
1. ![](https://www.google.com/s2/favicons?domain=https://www.twitter.com/) [Twitter (https://www.twitter.com/)](https://www.twitter.com/)*: top 5, messaging, social*
1. ![](https://www.google.com/s2/favicons?domain=https://linkedin.com) [LinkedIn (https://linkedin.com)](https://linkedin.com)*: top 6, professional, social*
@@ -21,7 +22,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://wordpress.com) [WordPress (https://wordpress.com)](https://wordpress.com)*: top 50, blog*
1. ![](https://www.google.com/s2/favicons?domain=https://plus.google.com) [Google Plus (archived) (https://plus.google.com)](https://plus.google.com)*: top 50, social*
1. ![](https://www.google.com/s2/favicons?domain=https://t.me/) [Telegram (https://t.me/)](https://t.me/)*: top 50, messaging*
1. ![](https://www.google.com/s2/favicons?domain=https://www.reddit.com/) [Reddit (https://www.reddit.com/)](https://www.reddit.com/)*: top 50, discussion, news, social*
1. ![](https://www.google.com/s2/favicons?domain=https://www.reddit.com/) [Reddit (https://www.reddit.com/)](https://www.reddit.com/)*: top 50, discussion, news, social*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://www.tumblr.com) [Tumblr (https://www.tumblr.com)](https://www.tumblr.com)*: top 100, blog, social*
1. ![](https://www.google.com/s2/favicons?domain=https://open.spotify.com/) [Spotify (https://open.spotify.com/)](https://open.spotify.com/)*: top 100, music*
1. ![](https://www.google.com/s2/favicons?domain=https://archive.org) [Archive.org (https://archive.org)](https://archive.org)*: top 100, archive*, search is disabled
@@ -100,7 +101,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://www.op.gg/) [OP.GG LoL Vietnam (https://www.op.gg/)](https://www.op.gg/)*: top 500, gaming, vn*
1. ![](https://www.google.com/s2/favicons?domain=https://www.op.gg/) [OP.GG LoL Thailand (https://www.op.gg/)](https://www.op.gg/)*: top 500, gaming, th*
1. ![](https://www.google.com/s2/favicons?domain=https://www.xing.com/) [Xing (https://www.xing.com/)](https://www.xing.com/)*: top 500, de, eu*
1. ![](https://www.google.com/s2/favicons?domain=https://www.patreon.com/) [Patreon (https://www.patreon.com/)](https://www.patreon.com/)*: top 500, finance*
1. ![](https://www.google.com/s2/favicons?domain=https://www.patreon.com/) [Patreon (https://www.patreon.com/)](https://www.patreon.com/)*: top 500, finance*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://deviantart.com) [DeviantART (https://deviantart.com)](https://deviantart.com)*: top 500, art, photo*
1. ![](https://www.google.com/s2/favicons?domain=https://www.gofundme.com) [Gofundme (https://www.gofundme.com)](https://www.gofundme.com)*: top 500, finance*
1. ![](https://www.google.com/s2/favicons?domain=https://www.zhihu.com/) [Zhihu (https://www.zhihu.com/)](https://www.zhihu.com/)*: top 500, cn*, search is disabled
@@ -170,7 +171,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://www.liveinternet.ru) [LiveInternet (https://www.liveinternet.ru)](https://www.liveinternet.ru)*: top 5K, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://www.buymeacoffee.com/) [BuyMeACoffee (https://www.buymeacoffee.com/)](https://www.buymeacoffee.com/)*: top 5K, freelance*
1. ![](https://www.google.com/s2/favicons?domain=https://gitea.com/) [Gitea (https://gitea.com/)](https://gitea.com/)*: top 5K, coding*
1. ![](https://www.google.com/s2/favicons?domain=https://genius.com/) [Genius (https://genius.com/)](https://genius.com/)*: top 5K, music*
1. ![](https://www.google.com/s2/favicons?domain=https://genius.com/) [Genius (https://genius.com/)](https://genius.com/)*: top 5K, music*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://www.techrepublic.com) [Techrepublic (https://www.techrepublic.com)](https://www.techrepublic.com)*: top 5K, news, tech*
1. ![](https://www.google.com/s2/favicons?domain=https://hubpages.com/) [HubPages (https://hubpages.com/)](https://hubpages.com/)*: top 5K, blog*
1. ![](https://www.google.com/s2/favicons?domain=https://www.artstation.com) [Artstation (https://www.artstation.com)](https://www.artstation.com)*: top 5K, art, stock*
@@ -182,7 +183,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://www.alltrails.com/) [AllTrails (https://www.alltrails.com/)](https://www.alltrails.com/)*: top 5K, sport, travel*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://habr.com/) [Habr (https://habr.com/)](https://habr.com/)*: top 5K, blog, discussion, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://www.allrecipes.com/) [AllRecipes (https://www.allrecipes.com/)](https://www.allrecipes.com/)*: top 5K, hobby*
1. ![](https://www.google.com/s2/favicons?domain=https://www.redbubble.com/) [Redbubble (https://www.redbubble.com/)](https://www.redbubble.com/)*: top 5K, shopping*
1. ![](https://www.google.com/s2/favicons?domain=https://www.redbubble.com/) [Redbubble (https://www.redbubble.com/)](https://www.redbubble.com/)*: top 5K, shopping*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://www.diigo.com/) [Diigo (https://www.diigo.com/)](https://www.diigo.com/)*: top 5K, bookmarks*
1. ![](https://www.google.com/s2/favicons?domain=https://windy.com/) [Windy (https://windy.com/)](https://windy.com/)*: top 5K, maps*
1. ![](https://www.google.com/s2/favicons?domain=https://codecanyon.net) [Codecanyon (https://codecanyon.net)](https://codecanyon.net)*: top 5K, coding, shopping*
@@ -270,9 +271,9 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://hackaday.io/) [Hackaday (https://hackaday.io/)](https://hackaday.io/)*: top 5K, hobby, tech*
1. ![](https://www.google.com/s2/favicons?domain=https://www.animenewsnetwork.com) [AnimeNewsNetwork (https://www.animenewsnetwork.com)](https://www.animenewsnetwork.com)*: top 5K, anime, news*
1. ![](https://www.google.com/s2/favicons?domain=https://www.librarything.com/) [LibraryThing (https://www.librarything.com/)](https://www.librarything.com/)*: top 5K, books*
1. ![](https://www.google.com/s2/favicons?domain=https://www.fodors.com) [Fodors (https://www.fodors.com)](https://www.fodors.com)*: top 5K, travel*
1. ![](https://www.google.com/s2/favicons?domain=https://www.fodors.com) [Fodors (https://www.fodors.com)](https://www.fodors.com)*: top 5K, travel*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://99designs.com) [Designs99 (https://99designs.com)](https://99designs.com)*: top 5K, design, photo*
1. ![](https://www.google.com/s2/favicons?domain=https://www.pscp.tv) [Periscope (https://www.pscp.tv)](https://www.pscp.tv)*: top 5K, streaming, video*
1. ![](https://www.google.com/s2/favicons?domain=https://www.pscp.tv) [PeriscopeTv (https://www.pscp.tv)](https://www.pscp.tv)*: top 5K, streaming, video*
1. ![](https://www.google.com/s2/favicons?domain=https://freesound.org/) [Freesound (https://freesound.org/)](https://freesound.org/)*: top 5K, music*
1. ![](https://www.google.com/s2/favicons?domain=https://www.metal-archives.com) [Metal-archives (https://www.metal-archives.com)](https://www.metal-archives.com)*: top 5K, music*
1. ![](https://www.google.com/s2/favicons?domain=https://www.kongregate.com/) [Kongregate (https://www.kongregate.com/)](https://www.kongregate.com/)*: top 5K, gaming*
@@ -302,7 +303,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://www.ebaumsworld.com/) [eBaumsWorld (https://www.ebaumsworld.com/)](https://www.ebaumsworld.com/)*: top 10K, news*
1. ![](https://www.google.com/s2/favicons?domain=https://rapidapi.com) [RapidAPI (https://rapidapi.com)](https://rapidapi.com)*: top 10K, coding*
1. ![](https://www.google.com/s2/favicons?domain=https://www.tbank.ru/invest/) [Tinkoff Invest (https://www.tbank.ru/invest/)](https://www.tbank.ru/invest/)*: top 10K, ru*
1. ![](https://www.google.com/s2/favicons?domain=http://ccmixter.org/) [Ccmixter (http://ccmixter.org/)](http://ccmixter.org/)*: top 10K, music*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=http://ccmixter.org/) [Ccmixter (http://ccmixter.org/)](http://ccmixter.org/)*: top 10K, music*
1. ![](https://www.google.com/s2/favicons?domain=https://dtf.ru) [DTF (https://dtf.ru)](https://dtf.ru)*: top 10K, ru*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://www.minds.com) [Minds (https://www.minds.com)](https://www.minds.com)*: top 10K, in, social*
1. ![](https://www.google.com/s2/favicons?domain=https://www.fanpop.com/) [Fanpop (https://www.fanpop.com/)](https://www.fanpop.com/)*: top 10K, discussion*, search is disabled
@@ -339,14 +340,14 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://weheartit.com/) [We Heart It (https://weheartit.com/)](https://weheartit.com/)*: top 10K, blog, photo*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://www.theodysseyonline.com) [TheOdysseyOnline (https://www.theodysseyonline.com)](https://www.theodysseyonline.com)*: top 10K, blog*
1. ![](https://www.google.com/s2/favicons?domain=https://www.kaskus.co.id) [Kaskus (https://www.kaskus.co.id)](https://www.kaskus.co.id)*: top 10K, id*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://www.periscope.tv/) [PeriscopeMain (https://www.periscope.tv/)](https://www.periscope.tv/)*: top 10K, streaming, video*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://www.periscope.tv/) [Periscope (https://www.periscope.tv/)](https://www.periscope.tv/)*: top 10K, streaming, video*
1. ![](https://www.google.com/s2/favicons?domain=https://mssg.me) [mssg.me (https://mssg.me)](https://mssg.me)*: top 10K, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://www.nairaland.com/) [Nairaland Forum (https://www.nairaland.com/)](https://www.nairaland.com/)*: top 10K, ng*
1. ![](https://www.google.com/s2/favicons?domain=https://www.sports.ru/) [sports.ru (https://www.sports.ru/)](https://www.sports.ru/)*: top 10K, ru, sport*
1. ![](https://www.google.com/s2/favicons?domain=https://banki.ru) [banki.ru (https://banki.ru)](https://banki.ru)*: top 10K, finance, ru*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=http://colourlovers.com) [ColourLovers (http://colourlovers.com)](http://colourlovers.com)*: top 10K, design*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://picsart.com/) [Picsart (https://picsart.com/)](https://picsart.com/)*: top 10K, photo*
1. ![](https://www.google.com/s2/favicons?domain=https://codesandbox.io) [Code Sandbox (https://codesandbox.io)](https://codesandbox.io)*: top 10K, coding*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://codesandbox.io) [Code Sandbox (https://codesandbox.io)](https://codesandbox.io)*: top 10K, coding*
1. ![](https://www.google.com/s2/favicons?domain=https://www.skyscrapercity.com) [SkyscraperCity (https://www.skyscrapercity.com)](https://www.skyscrapercity.com)*: top 10K, forum*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://www.drive2.ru/) [Drive2 (https://www.drive2.ru/)](https://www.drive2.ru/)*: top 10K, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://www.wowhead.com) [Wowhead (https://www.wowhead.com)](https://www.wowhead.com)*: top 10K, gaming*
@@ -415,13 +416,13 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://www.thestudentroom.co.uk) [TheStudentRoom (https://www.thestudentroom.co.uk)](https://www.thestudentroom.co.uk)*: top 100K, forum, gb*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://www.codementor.io/) [Codementor (https://www.codementor.io/)](https://www.codementor.io/)*: top 100K, coding*
1. ![](https://www.google.com/s2/favicons?domain=https://n4g.com/) [N4g (https://n4g.com/)](https://n4g.com/)*: top 100K, gaming, news*
1. ![](https://www.google.com/s2/favicons?domain=https://www.lomography.com) [Lomography (https://www.lomography.com)](https://www.lomography.com)*: top 100K, photo*
1. ![](https://www.google.com/s2/favicons?domain=https://www.lomography.com) [Lomography (https://www.lomography.com)](https://www.lomography.com)*: top 100K, photo*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://pixelfed.social/) [pixelfed.social (https://pixelfed.social/)](https://pixelfed.social/)*: top 100K, art, photo*
1. ![](https://www.google.com/s2/favicons?domain=https://www.hackerearth.com) [Hackerearth (https://www.hackerearth.com)](https://www.hackerearth.com)*: top 100K, freelance*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://weedmaps.com) [Weedmaps (https://weedmaps.com)](https://weedmaps.com)*: top 100K, us*
1. ![](https://www.google.com/s2/favicons?domain=https://www.redtube.com/) [Redtube (https://www.redtube.com/)](https://www.redtube.com/)*: top 100K, porn*
1. ![](https://www.google.com/s2/favicons?domain=https://www.neoseeker.com) [Neoseeker (https://www.neoseeker.com)](https://www.neoseeker.com)*: top 100K, forum, gaming*
1. ![](https://www.google.com/s2/favicons?domain=https://liberapay.com) [Liberapay (https://liberapay.com)](https://liberapay.com)*: top 100K, finance*
1. ![](https://www.google.com/s2/favicons?domain=https://liberapay.com) [Liberapay (https://liberapay.com)](https://liberapay.com)*: top 100K, finance*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://www.sythe.org) [Sythe (https://www.sythe.org)](https://www.sythe.org)*: top 100K, forum*
1. ![](https://www.google.com/s2/favicons?domain=https://www.filmweb.pl/user/adam) [FilmWeb (https://www.filmweb.pl/user/adam)](https://www.filmweb.pl/user/adam)*: top 100K, movies, pl*
1. ![](https://www.google.com/s2/favicons?domain=https://listal.com/) [Listal (https://listal.com/)](https://listal.com/)*: top 100K, movies, music*
@@ -430,7 +431,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://www.spatial.io) [Spatial (https://www.spatial.io)](https://www.spatial.io)*: top 100K, crypto, gaming*
1. ![](https://www.google.com/s2/favicons?domain=https://www.nn.ru/) [NN.RU (https://www.nn.ru/)](https://www.nn.ru/)*: top 100K, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://paragraph.com) [Paragraph (https://paragraph.com)](https://paragraph.com)*: top 100K, blog, crypto*
1. ![](https://www.google.com/s2/favicons?domain=https://www.huntingnet.com) [Huntingnet (https://www.huntingnet.com)](https://www.huntingnet.com)*: top 100K, us*
1. ![](https://www.google.com/s2/favicons?domain=https://www.huntingnet.com) [Huntingnet (https://www.huntingnet.com)](https://www.huntingnet.com)*: top 100K, us*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://telescope.ac) [telescope.ac (https://telescope.ac)](https://telescope.ac)*: top 100K, blog*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://chaos.social/) [chaos.social (https://chaos.social/)](https://chaos.social/)*: top 100K, social*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://chaos.social/) [mastodon.social (https://chaos.social/)](https://chaos.social/)*: top 100K, social*
@@ -522,7 +523,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://mastodon.cloud/) [mastodon.cloud (https://mastodon.cloud/)](https://mastodon.cloud/)*: top 100K, pk*
1. ![](https://www.google.com/s2/favicons?domain=https://1x.com) [1x (https://1x.com)](https://1x.com)*: top 100K, photo*
1. ![](https://www.google.com/s2/favicons?domain=https://www.patientslikeme.com) [PatientsLikeMe (https://www.patientslikeme.com)](https://www.patientslikeme.com)*: top 100K, medicine, us*
1. ![](https://www.google.com/s2/favicons?domain=https://www.tikvib.com/) [Picuki (https://www.tikvib.com/)](https://www.tikvib.com/)*: top 100K, video*
1. ![](https://www.google.com/s2/favicons?domain=https://www.picuki.com/) [Picuki (https://www.picuki.com/)](https://www.picuki.com/)*: top 100K, photo*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://www.pokecommunity.com) [Pokecommunity (https://www.pokecommunity.com)](https://www.pokecommunity.com)*: top 100K, forum, gaming*
1. ![](https://www.google.com/s2/favicons?domain=https://eintracht.de) [Eintracht (https://eintracht.de)](https://eintracht.de)*: top 100K, tr*
1. ![](https://www.google.com/s2/favicons?domain=https://www.datpiff.com) [Datpiff (https://www.datpiff.com)](https://www.datpiff.com)*: top 100K, us*
@@ -623,14 +624,14 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://mywed.com/ru) [Mywed (https://mywed.com/ru)](https://mywed.com/ru)*: top 100K, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://golbis.com) [Golbis (https://golbis.com)](https://golbis.com)*: top 100K, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://www.sooplive.co.kr/) [Soop (https://www.sooplive.co.kr/)](https://www.sooplive.co.kr/)*: top 100K, kr*
1. ![](https://www.google.com/s2/favicons?domain=https://freelancehunt.com) [Freelancehunt (https://freelancehunt.com)](https://freelancehunt.com)*: top 100K, freelance, ru, ua*
1. ![](https://www.google.com/s2/favicons?domain=https://freelancehunt.com) [Freelancehunt (https://freelancehunt.com)](https://freelancehunt.com)*: top 100K, freelance, ru, ua*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://atcoder.jp/) [Atcoder (https://atcoder.jp/)](https://atcoder.jp/)*: top 100K, coding, jp*
1. ![](https://www.google.com/s2/favicons?domain=https://www.livejasmin.com/) [Livejasmin (https://www.livejasmin.com/)](https://www.livejasmin.com/)*: top 100K, us, webcam*
1. ![](https://www.google.com/s2/favicons?domain=https://wanelo.com/) [Wanelo (https://wanelo.com/)](https://wanelo.com/)*: top 100K, shopping*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://motherless.com/) [Motherless (https://motherless.com/)](https://motherless.com/)*: top 100K, porn*
1. ![](https://www.google.com/s2/favicons?domain=http://fanlore.org) [Fanlore (http://fanlore.org)](http://fanlore.org)*: top 100K, us*
1. ![](https://www.google.com/s2/favicons?domain=http://fanlore.org) [Fanlore (http://fanlore.org)](http://fanlore.org)*: top 100K, us*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://www.jetpunk.com) [Jetpunk (https://www.jetpunk.com)](https://www.jetpunk.com)*: top 100K, gaming*
1. ![](https://www.google.com/s2/favicons?domain=https://icobench.com) [Icobench (https://icobench.com)](https://icobench.com)*: top 100K, kr, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://icobench.com) [Icobench (https://icobench.com)](https://icobench.com)*: top 100K, kr, ru*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://www.rappad.co) [Rappad (https://www.rappad.co)](https://www.rappad.co)*: top 100K, music*
1. ![](https://www.google.com/s2/favicons?domain=https://maxpark.com) [Maxpark (https://maxpark.com)](https://maxpark.com)*: top 100K, news, ru*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://savingadvice.com) [savingadvice.com (https://savingadvice.com)](https://savingadvice.com)*: top 100K, finance*
@@ -671,7 +672,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://rmmedia.ru) [Rmmedia (https://rmmedia.ru)](https://rmmedia.ru)*: top 100K, forum, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://trashbox.ru/) [Trashbox.ru (https://trashbox.ru/)](https://trashbox.ru/)*: top 100K, az, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://www.ddo.com) [Ddo (https://www.ddo.com)](https://www.ddo.com)*: top 100K, forum*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://www.hometheaterforum.com) [Hometheaterforum (https://www.hometheaterforum.com)](https://www.hometheaterforum.com)*: top 100K, forum, us*
1. ![](https://www.google.com/s2/favicons?domain=https://www.hometheaterforum.com) [Hometheaterforum (https://www.hometheaterforum.com)](https://www.hometheaterforum.com)*: top 100K, forum, us*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://www.vlr.gg) [VLR (https://www.vlr.gg)](https://www.vlr.gg)*: top 100K, gaming*
1. ![](https://www.google.com/s2/favicons?domain=https://www.hackingwithswift.com) [HackingWithSwift (https://www.hackingwithswift.com)](https://www.hackingwithswift.com)*: top 100K, coding*
1. ![](https://www.google.com/s2/favicons?domain=https://partyflock.nl) [Partyflock (https://partyflock.nl)](https://partyflock.nl)*: top 100K, nl*
@@ -682,7 +683,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://www.medikforum.ru) [Medikforum (https://www.medikforum.ru)](https://www.medikforum.ru)*: top 100K, de, forum, nl, ru, ua*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://mynickname.com) [mynickname.com (https://mynickname.com)](https://mynickname.com)*: top 100K, social*
1. ![](https://www.google.com/s2/favicons?domain=https://appleinsider.ru) [appleinsider.ru (https://appleinsider.ru)](https://appleinsider.ru)*: top 100K, news, ru, tech*
1. ![](https://www.google.com/s2/favicons?domain=https://imginn.com) [ImgInn (https://imginn.com)](https://imginn.com)*: top 100K, photo*
1. ![](https://www.google.com/s2/favicons?domain=https://imginn.com) [ImgInn (https://imginn.com)](https://imginn.com)*: top 100K, photo*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://rpggeek.com) [RPGGeek (https://rpggeek.com)](https://rpggeek.com)*: top 100K, gaming*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://www.suomi24.fi) [Suomi24 (https://www.suomi24.fi)](https://www.suomi24.fi)*: top 100K, fi, jp*
1. ![](https://www.google.com/s2/favicons?domain=https://ethereum-magicians.org) [Ethereum-magicians (https://ethereum-magicians.org)](https://ethereum-magicians.org)*: top 100K, cr, forum*
@@ -763,7 +764,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://www.freelancejob.ru) [FreelanceJob (https://www.freelancejob.ru)](https://www.freelancejob.ru)*: top 10M, ru*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://www.rusfootball.info/) [Football (https://www.rusfootball.info/)](https://www.rusfootball.info/)*: top 10M, ru*
1. ![](https://www.google.com/s2/favicons?domain=http://www.beerintheevening.com) [Beerintheevening (http://www.beerintheevening.com)](http://www.beerintheevening.com)*: top 10M, gb*
1. ![](https://www.google.com/s2/favicons?domain=https://fortnitetracker.com/challenges) [FortniteTracker (https://fortnitetracker.com/challenges)](https://fortnitetracker.com/challenges)*: top 10M, gaming*
1. ![](https://www.google.com/s2/favicons?domain=https://fortnitetracker.com/challenges) [FortniteTracker (https://fortnitetracker.com/challenges)](https://fortnitetracker.com/challenges)*: top 10M, gaming*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://www.heavy-r.com/) [Heavy R (https://www.heavy-r.com/)](https://www.heavy-r.com/)*: top 10M, porn*
1. ![](https://www.google.com/s2/favicons?domain=http://www.coolminiornot.com) [Coolminiornot (http://www.coolminiornot.com)](http://www.coolminiornot.com)*: top 10M, forum, sg*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://www.1001tracklists.com) [1001tracklists (https://www.1001tracklists.com)](https://www.1001tracklists.com)*: top 10M, music*
@@ -779,7 +780,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://professionali.ru) [Professionali (https://professionali.ru)](https://professionali.ru)*: top 10M, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://listography.com/adam) [Listography (https://listography.com/adam)](https://listography.com/adam)*: top 10M, sharing*
1. ![](https://www.google.com/s2/favicons?domain=https://www.theanswerbank.co.uk) [The AnswerBank (https://www.theanswerbank.co.uk)](https://www.theanswerbank.co.uk)*: top 10M, gb, q&a*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://www.bdoutdoors.com) [Bdoutdoors (https://www.bdoutdoors.com)](https://www.bdoutdoors.com)*: top 10M, us*
1. ![](https://www.google.com/s2/favicons?domain=https://www.bdoutdoors.com) [Bdoutdoors (https://www.bdoutdoors.com)](https://www.bdoutdoors.com)*: top 10M, us*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=http://millerovo161.ru) [millerovo161.ru (http://millerovo161.ru)](http://millerovo161.ru)*: top 10M, forum, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://shikimori.one) [Shikimori (https://shikimori.one)](https://shikimori.one)*: top 10M, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://www.kharkovforum.com/) [KharkovForum (https://www.kharkovforum.com/)](https://www.kharkovforum.com/)*: top 10M, forum, ua*, search is disabled
@@ -796,7 +797,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://www.fluther.com/) [Fluther (https://www.fluther.com/)](https://www.fluther.com/)*: top 10M, q&a*
1. ![](https://www.google.com/s2/favicons?domain=https://www.sbazar.cz/) [Sbazar.cz (https://www.sbazar.cz/)](https://www.sbazar.cz/)*: top 10M, cz, shopping*
1. ![](https://www.google.com/s2/favicons?domain=https://vintage-mustang.com) [vintage-mustang.com (https://vintage-mustang.com)](https://vintage-mustang.com)*: top 10M, forum, us*
1. ![](https://www.google.com/s2/favicons?domain=https://www.forum.hr) [forum.hr (https://www.forum.hr)](https://www.forum.hr)*: top 10M, forum, hr*
1. ![](https://www.google.com/s2/favicons?domain=http://www.forum.hr) [forum.hr (http://www.forum.hr)](http://www.forum.hr)*: top 10M, forum, hr*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=http://school2dobrinka.ru) [school2dobrinka.ru (http://school2dobrinka.ru)](http://school2dobrinka.ru)*: top 10M, education, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://kosmetista.ru) [Kosmetista (https://kosmetista.ru)](https://kosmetista.ru)*: top 10M, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://www.pbnation.com/) [Pbnation (https://www.pbnation.com/)](https://www.pbnation.com/)*: top 10M, ca*, search is disabled
@@ -842,7 +843,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://www.hsx.com) [HSX (https://www.hsx.com)](https://www.hsx.com)*: top 10M, finance*
1. ![](https://www.google.com/s2/favicons?domain=https://bobrdobr.ru) [Bobrdobr (https://bobrdobr.ru)](https://bobrdobr.ru)*: top 10M, az, ru, tr, ua*
1. ![](https://www.google.com/s2/favicons?domain=https://www.blipfoto.com) [Blipfoto (https://www.blipfoto.com)](https://www.blipfoto.com)*: top 10M, photo*
1. ![](https://www.google.com/s2/favicons?domain=https://www.romanticcollection.ru) [RomanticCollection (https://www.romanticcollection.ru)](https://www.romanticcollection.ru)*: top 10M, ru*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://www.romanticcollection.ru) [RomanticCollection (https://www.romanticcollection.ru)](https://www.romanticcollection.ru)*: top 10M, ru*
1. ![](https://www.google.com/s2/favicons?domain=http://justmj.ru) [justmj.ru (http://justmj.ru)](http://justmj.ru)*: top 10M, blog, ru*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://www.edugeek.net) [EduGeek (https://www.edugeek.net)](https://www.edugeek.net)*: top 10M, education*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://ww2aircraft.net/forum/) [ww2aircraft.net (https://ww2aircraft.net/forum/)](https://ww2aircraft.net/forum/)*: top 10M, forum*
@@ -880,7 +881,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://proglib.io) [Proglib (https://proglib.io)](https://proglib.io)*: top 10M, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://nightbot.tv/) [nightbot (https://nightbot.tv/)](https://nightbot.tv/)*: top 10M, jp*
1. ![](https://www.google.com/s2/favicons?domain=https://www.hunttalk.com) [Hunttalk (https://www.hunttalk.com)](https://www.hunttalk.com)*: top 10M, forum, us*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://dmoj.ca/) [DMOJ (https://dmoj.ca/)](https://dmoj.ca/)*: top 10M, ca, coding*
1. ![](https://www.google.com/s2/favicons?domain=https://dmoj.ca/) [DMOJ (https://dmoj.ca/)](https://dmoj.ca/)*: top 10M, ca, coding*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://truesteamachievements.com) [Truesteamachievements (https://truesteamachievements.com)](https://truesteamachievements.com)*: top 10M, az, gb*
1. ![](https://www.google.com/s2/favicons?domain=https://www.thefastlaneforum.com) [TheFastlaneForum (https://www.thefastlaneforum.com)](https://www.thefastlaneforum.com)*: top 10M, forum, us*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=http://www.lada-vesta.net) [lada-vesta.net (http://www.lada-vesta.net)](http://www.lada-vesta.net)*: top 10M, auto, forum, ru*
@@ -944,7 +945,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://www.gps-data-team.com) [Gps-data-team (https://www.gps-data-team.com)](https://www.gps-data-team.com)*: top 10M, maps*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://yasobe.ru) [Soberu (https://yasobe.ru)](https://yasobe.ru)*: top 10M, ru*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://www.imood.com/) [Imood (https://www.imood.com/)](https://www.imood.com/)*: top 10M, blog*
1. ![](https://www.google.com/s2/favicons?domain=https://elakiri.com) [Elakiri (https://elakiri.com)](https://elakiri.com)*: top 10M, lk*
1. ![](https://www.google.com/s2/favicons?domain=https://elakiri.com) [Elakiri (https://elakiri.com)](https://elakiri.com)*: top 10M, lk*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://www.countable.us/) [Countable (https://www.countable.us/)](https://www.countable.us/)*: top 10M, us*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://www.shipmodeling.ru/phpbb) [shipmodeling.ru (https://www.shipmodeling.ru/phpbb)](https://www.shipmodeling.ru/phpbb)*: top 10M, forum, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://armtorg.ru/) [Armtorg (https://armtorg.ru/)](https://armtorg.ru/)*: top 10M, forum, ru*
@@ -979,7 +980,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://www.mdshooters.com) [Mdshooters (https://www.mdshooters.com)](https://www.mdshooters.com)*: top 10M, forum, us*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://prodaman.ru) [Prodaman (https://prodaman.ru)](https://prodaman.ru)*: top 10M, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://mikrob.ru) [mikrob.ru (https://mikrob.ru)](https://mikrob.ru)*: top 10M, forum, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://www.gardrops.com) [Gardrops (https://www.gardrops.com)](https://www.gardrops.com)*: top 10M, shopping, tr*
1. ![](https://www.google.com/s2/favicons?domain=https://www.gardrops.com) [Gardrops (https://www.gardrops.com)](https://www.gardrops.com)*: top 10M, shopping, tr*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://zagony.ru) [Zagony (https://zagony.ru)](https://zagony.ru)*: top 10M, ru*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://pogovorim.by) [Pogovorim (https://pogovorim.by)](https://pogovorim.by)*: top 10M, by, ru*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://sniperforums.com) [sniperforums.com (https://sniperforums.com)](https://sniperforums.com)*: top 10M, forum*
@@ -1038,7 +1039,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://www.prokoni.ru/) [Prokoni (https://www.prokoni.ru/)](https://www.prokoni.ru/)*: top 10M, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://donate.stream/) [donate.stream (https://donate.stream/)](https://donate.stream/)*: top 10M, finance, ru*
1. ![](https://www.google.com/s2/favicons?domain=http://directx10.org) [directx10.org (http://directx10.org)](http://directx10.org)*: top 10M*
1. ![](https://www.google.com/s2/favicons?domain=https://www.allthingsworn.com) [All Things Worn (https://www.allthingsworn.com)](https://www.allthingsworn.com)*: top 10M*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://www.allthingsworn.com) [All Things Worn (https://www.allthingsworn.com)](https://www.allthingsworn.com)*: top 10M*
1. ![](https://www.google.com/s2/favicons?domain=http://www.forumjizni.ru) [ForumJizni (http://www.forumjizni.ru)](http://www.forumjizni.ru)*: top 10M, forum, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://apclips.com/) [APClips (https://apclips.com/)](https://apclips.com/)*: top 10M, porn, video*
1. ![](https://www.google.com/s2/favicons?domain=https://xgm.guru) [xgm.guru (https://xgm.guru)](https://xgm.guru)*: top 10M, forum, gaming*
@@ -1075,7 +1076,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://qna.center) [qna.center (https://qna.center)](https://qna.center)*: top 10M, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://spaces.im) [Spaces (https://spaces.im)](https://spaces.im)*: top 10M, blog, ru*
1. ![](https://www.google.com/s2/favicons?domain=http://peopleandcountries.com) [PeopleAndCountries (http://peopleandcountries.com)](http://peopleandcountries.com)*: top 10M, forum, ru*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://subforums.net) [subforums.net (https://subforums.net)](https://subforums.net)*: top 10M, forum*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://subforums.net) [subforums.net (https://subforums.net)](https://subforums.net)*: top 10M, forum*
1. ![](https://www.google.com/s2/favicons?domain=https://pornsavant.com) [pornsavant.com (https://pornsavant.com)](https://pornsavant.com)*: top 10M, forum*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=http://www.figarohair.ru/conf) [figarohair.ru (http://www.figarohair.ru/conf)](http://www.figarohair.ru/conf)*: top 10M, forum, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://lenov.ru) [Lenov (https://lenov.ru)](https://lenov.ru)*: top 10M, ru*
@@ -1087,7 +1088,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://24open.ru) [24open (https://24open.ru)](https://24open.ru)*: top 10M, dating, ru*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://www.xtratime.org) [xtratime.org (https://www.xtratime.org)](https://www.xtratime.org)*: top 10M, forum*
1. ![](https://www.google.com/s2/favicons?domain=http://www.totseans.com/bbs/profile/Vizier) [Totseans (http://www.totseans.com/bbs/profile/Vizier)](http://www.totseans.com/bbs/profile/Vizier)*: top 10M, forum*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://www.browncafe.com/community/) [browncafe.com (https://www.browncafe.com/community/)](https://www.browncafe.com/community/)*: top 10M, forum*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=http://www.browncafe.com/community/) [browncafe.com (http://www.browncafe.com/community/)](http://www.browncafe.com/community/)*: top 10M, forum*
1. ![](https://www.google.com/s2/favicons?domain=https://www.nikoncafe.com/) [nikoncafe.com (https://www.nikoncafe.com/)](https://www.nikoncafe.com/)*: top 10M, forum, photo*
1. ![](https://www.google.com/s2/favicons?domain=https://crafta.ua) [crafta.ua (https://crafta.ua)](https://crafta.ua)*: top 10M, ua*
1. ![](https://www.google.com/s2/favicons?domain=http://www.scaleforum.ru) [scaleforum.ru (http://www.scaleforum.ru)](http://www.scaleforum.ru)*: top 10M*, search is disabled
@@ -1191,10 +1192,10 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://tieba.baidu.com) [Baidu (https://tieba.baidu.com)](https://tieba.baidu.com)*: top 100M, cn*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://community.oracle.com) [Oracle Community (https://community.oracle.com)](https://community.oracle.com)*: top 100M*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://forums.opera.com/) [forums.opera.com (https://forums.opera.com/)](https://forums.opera.com/)*: top 100M, forum*
1. ![](https://www.google.com/s2/favicons?domain=https://community.cloudflare.com/) [CloudflareCommunity (https://community.cloudflare.com/)](https://community.cloudflare.com/)*: top 100M, forum, tech*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://community.cloudflare.com/) [CloudflareCommunity (https://community.cloudflare.com/)](https://community.cloudflare.com/)*: top 100M, forum, tech*
1. ![](https://www.google.com/s2/favicons?domain=https://scratch.mit.edu/) [Scratch (https://scratch.mit.edu/)](https://scratch.mit.edu/)*: top 100M, coding*
1. ![](https://www.google.com/s2/favicons?domain=https://cyber.harvard.edu) [cyber.harvard.edu (https://cyber.harvard.edu)](https://cyber.harvard.edu)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=https://scholar.harvard.edu/) [Harvard Scholar (https://scholar.harvard.edu/)](https://scholar.harvard.edu/)*: top 100M*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://scholar.harvard.edu/) [Harvard Scholar (https://scholar.harvard.edu/)](https://scholar.harvard.edu/)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=https://market.yandex.ru/) [YandexMarket (https://market.yandex.ru/)](https://market.yandex.ru/)*: top 100M, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://music.yandex.ru/) [YandexMusic (https://music.yandex.ru/)](https://music.yandex.ru/)*: top 100M, music, ru*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://zen.yandex.ru) [YandexZenUser (https://zen.yandex.ru)](https://zen.yandex.ru)*: top 100M, ru*
@@ -1232,10 +1233,11 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://wf.mail.ru) [Warface (https://wf.mail.ru)](https://wf.mail.ru)*: top 100M, forum, ru*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://people.ign.com/) [PeopleIgn (https://people.ign.com/)](https://people.ign.com/)*: top 100M, gaming*
1. ![](https://www.google.com/s2/favicons?domain=https://forums.envato.com) [Envato (https://forums.envato.com)](https://forums.envato.com)*: top 100M, au, forum*
1. ![](https://www.google.com/s2/favicons?domain=https://2berega.spb.ru) [2berega.spb.ru (https://2berega.spb.ru)](https://2berega.spb.ru)*: top 100M, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://diveforum.spb.ru/) [Diveforum (https://diveforum.spb.ru/)](https://diveforum.spb.ru/)*: top 100M, forum, ru*
1. ![](https://www.google.com/s2/favicons?domain=http://poteryashka.spb.ru) [poteryashka.spb.ru (http://poteryashka.spb.ru)](http://poteryashka.spb.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=https://forums.ea.com) [forums.ea.com (https://forums.ea.com)](https://forums.ea.com)*: top 100M, forum, gaming*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://comicvine.gamespot.com/) [ComicvineGamespot (https://comicvine.gamespot.com/)](https://comicvine.gamespot.com/)*: top 100M, gaming*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://comicvine.gamespot.com/) [ComicvineGamespot (https://comicvine.gamespot.com/)](https://comicvine.gamespot.com/)*: top 100M, gaming*
1. ![](https://www.google.com/s2/favicons?domain=https://gamefaqs.gamespot.com) [Gamefaqs (https://gamefaqs.gamespot.com)](https://gamefaqs.gamespot.com)*: top 100M, gaming*
1. ![](https://www.google.com/s2/favicons?domain=https://forum.sketchfab.com) [forum.sketchfab.com (https://forum.sketchfab.com)](https://forum.sketchfab.com)*: top 100M, forum*
1. ![](https://www.google.com/s2/favicons?domain=https://forum.pkp.sfu.ca) [forum.pkp.sfu.ca (https://forum.pkp.sfu.ca)](https://forum.pkp.sfu.ca)*: top 100M, ca, forum*
@@ -1264,7 +1266,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=http://www.active.lviv.ua) [active.lviv.ua (http://www.active.lviv.ua)](http://www.active.lviv.ua)*: top 100M, forum, ua*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=http://volkswagen.lviv.ua) [volkswagen.lviv.ua (http://volkswagen.lviv.ua)](http://volkswagen.lviv.ua)*: top 100M, auto, forum, ua*
1. ![](https://www.google.com/s2/favicons?domain=http://tuning.lviv.ua/forum) [tuning.lviv.ua (http://tuning.lviv.ua/forum)](http://tuning.lviv.ua/forum)*: top 100M, forum, ua*
1. ![](https://www.google.com/s2/favicons?domain=https://74507.ucoz.ru) [74507.ucoz.ru (https://74507.ucoz.ru)](https://74507.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://74507.ucoz.ru) [74507.ucoz.ru (http://74507.ucoz.ru)](http://74507.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=https://forum.ucoz.ru) [Ucoz (https://forum.ucoz.ru)](https://forum.ucoz.ru)*: top 100M, forum, ru*
1. ![](https://www.google.com/s2/favicons?domain=http://afsoc.ucoz.ru) [afsoc.ucoz.ru (http://afsoc.ucoz.ru)](http://afsoc.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://alpanf.ucoz.ru) [alpanf.ucoz.ru (http://alpanf.ucoz.ru)](http://alpanf.ucoz.ru)*: top 100M*
@@ -1312,23 +1314,23 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=http://milnerelena.ucoz.ru) [milnerelena.ucoz.ru (http://milnerelena.ucoz.ru)](http://milnerelena.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://zebest.ucoz.ru) [zebest.ucoz.ru (http://zebest.ucoz.ru)](http://zebest.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://hmkids.ucoz.ru) [hmkids.ucoz.ru (http://hmkids.ucoz.ru)](http://hmkids.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=https://shanson.ucoz.ru) [shanson.ucoz.ru (https://shanson.ucoz.ru)](https://shanson.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://shanson.ucoz.ru) [shanson.ucoz.ru (http://shanson.ucoz.ru)](http://shanson.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://klas-crew.ucoz.ru) [klas-crew.ucoz.ru (http://klas-crew.ucoz.ru)](http://klas-crew.ucoz.ru)*: top 100M*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=http://allmus.ucoz.ru) [allmus.ucoz.ru (http://allmus.ucoz.ru)](http://allmus.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://rotarusofi.ucoz.ru) [rotarusofi.ucoz.ru (http://rotarusofi.ucoz.ru)](http://rotarusofi.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://videomuzon.ucoz.ru) [videomuzon.ucoz.ru (http://videomuzon.ucoz.ru)](http://videomuzon.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://webmedia.ucoz.ru) [webmedia.ucoz.ru (http://webmedia.ucoz.ru)](http://webmedia.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=https://p1rat.ucoz.ru) [p1rat.ucoz.ru (https://p1rat.ucoz.ru)](https://p1rat.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://p1rat.ucoz.ru) [p1rat.ucoz.ru (http://p1rat.ucoz.ru)](http://p1rat.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://satisfacktion.ucoz.ru) [satisfacktion.ucoz.ru (http://satisfacktion.ucoz.ru)](http://satisfacktion.ucoz.ru)*: top 100M, music, ru*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=http://djfint.ucoz.ru) [djfint.ucoz.ru (http://djfint.ucoz.ru)](http://djfint.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://aviaforum.ucoz.ru) [aviaforum.ucoz.ru (http://aviaforum.ucoz.ru)](http://aviaforum.ucoz.ru)*: top 100M, forum*
1. ![](https://www.google.com/s2/favicons?domain=http://avia-forum.ucoz.ru) [avia-forum.ucoz.ru (http://avia-forum.ucoz.ru)](http://avia-forum.ucoz.ru)*: top 100M, forum, ru*
1. ![](https://www.google.com/s2/favicons?domain=http://terralight.ucoz.ru) [terralight.ucoz.ru (http://terralight.ucoz.ru)](http://terralight.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://tachograph.ucoz.ru) [tachograph.ucoz.ru (http://tachograph.ucoz.ru)](http://tachograph.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=https://moto-master.ucoz.ru) [moto-master.ucoz.ru (https://moto-master.ucoz.ru)](https://moto-master.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://moto-master.ucoz.ru) [moto-master.ucoz.ru (http://moto-master.ucoz.ru)](http://moto-master.ucoz.ru)*: top 100M*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=http://scb.ucoz.ru) [scb.ucoz.ru (http://scb.ucoz.ru)](http://scb.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://remzona-ekb.ucoz.ru) [remzona-ekb.ucoz.ru (http://remzona-ekb.ucoz.ru)](http://remzona-ekb.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=https://serwis.ucoz.ru) [serwis.ucoz.ru (https://serwis.ucoz.ru)](https://serwis.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://serwis.ucoz.ru) [serwis.ucoz.ru (http://serwis.ucoz.ru)](http://serwis.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://ankord.ucoz.ru) [ankord.ucoz.ru (http://ankord.ucoz.ru)](http://ankord.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://deutsch-auto68.ucoz.ru) [deutsch-auto68.ucoz.ru (http://deutsch-auto68.ucoz.ru)](http://deutsch-auto68.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://krum.ucoz.ru) [krum.ucoz.ru (http://krum.ucoz.ru)](http://krum.ucoz.ru)*: top 100M*
@@ -1379,7 +1381,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=http://gifts.ucoz.ru) [gifts.ucoz.ru (http://gifts.ucoz.ru)](http://gifts.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://sat-electronics.ucoz.ru) [sat-electronics.ucoz.ru (http://sat-electronics.ucoz.ru)](http://sat-electronics.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://pro-svet.ucoz.ru) [pro-svet.ucoz.ru (http://pro-svet.ucoz.ru)](http://pro-svet.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=https://memory57.ucoz.ru) [memory57.ucoz.ru (https://memory57.ucoz.ru)](https://memory57.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://memory57.ucoz.ru) [memory57.ucoz.ru (http://memory57.ucoz.ru)](http://memory57.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://baggi.ucoz.ru) [baggi.ucoz.ru (http://baggi.ucoz.ru)](http://baggi.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://gorbuha.ucoz.ru) [gorbuha.ucoz.ru (http://gorbuha.ucoz.ru)](http://gorbuha.ucoz.ru)*: top 100M*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=http://big-game.ucoz.ru) [big-game.ucoz.ru (http://big-game.ucoz.ru)](http://big-game.ucoz.ru)*: top 100M*
@@ -1410,7 +1412,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=http://collegy.ucoz.ru) [collegy.ucoz.ru (http://collegy.ucoz.ru)](http://collegy.ucoz.ru)*: top 100M, kz*
1. ![](https://www.google.com/s2/favicons?domain=http://anschula.ucoz.ru) [anschula.ucoz.ru (http://anschula.ucoz.ru)](http://anschula.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://garmin.ucoz.ru) [garmin.ucoz.ru (http://garmin.ucoz.ru)](http://garmin.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=https://655iap.ucoz.ru) [655iap.ucoz.ru (https://655iap.ucoz.ru)](https://655iap.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://655iap.ucoz.ru) [655iap.ucoz.ru (http://655iap.ucoz.ru)](http://655iap.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://el-pizza.ucoz.ru) [el-pizza.ucoz.ru (http://el-pizza.ucoz.ru)](http://el-pizza.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://berea.ucoz.ru) [berea.ucoz.ru (http://berea.ucoz.ru)](http://berea.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://zdorov10.ucoz.ru) [zdorov10.ucoz.ru (http://zdorov10.ucoz.ru)](http://zdorov10.ucoz.ru)*: top 100M*
@@ -1423,7 +1425,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=http://trainz-vl.ucoz.ru) [trainz-vl.ucoz.ru (http://trainz-vl.ucoz.ru)](http://trainz-vl.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://hulyaganka.ucoz.ru) [hulyaganka.ucoz.ru (http://hulyaganka.ucoz.ru)](http://hulyaganka.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://mix-best.ucoz.ru) [mix-best.ucoz.ru (http://mix-best.ucoz.ru)](http://mix-best.ucoz.ru)*: top 100M, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://cosmoforum.ucoz.ru) [cosmoforum.ucoz.ru (https://cosmoforum.ucoz.ru)](https://cosmoforum.ucoz.ru)*: top 100M, forum*
1. ![](https://www.google.com/s2/favicons?domain=http://cosmoforum.ucoz.ru) [cosmoforum.ucoz.ru (http://cosmoforum.ucoz.ru)](http://cosmoforum.ucoz.ru)*: top 100M, forum*
1. ![](https://www.google.com/s2/favicons?domain=http://mychildren.ucoz.ru) [mychildren.ucoz.ru (http://mychildren.ucoz.ru)](http://mychildren.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://icook.ucoz.ru) [icook.ucoz.ru (http://icook.ucoz.ru)](http://icook.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://dzhida2000.ucoz.ru) [dzhida2000.ucoz.ru (http://dzhida2000.ucoz.ru)](http://dzhida2000.ucoz.ru)*: top 100M*
@@ -1437,19 +1439,19 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=http://101vzvod.ucoz.ru) [101vzvod.ucoz.ru (http://101vzvod.ucoz.ru)](http://101vzvod.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://actikom.ucoz.ru) [actikom.ucoz.ru (http://actikom.ucoz.ru)](http://actikom.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://marym.ucoz.ru) [marym.ucoz.ru (http://marym.ucoz.ru)](http://marym.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=https://aikido-mariupol.ucoz.ru) [aikido-mariupol.ucoz.ru (https://aikido-mariupol.ucoz.ru)](https://aikido-mariupol.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://aikido-mariupol.ucoz.ru) [aikido-mariupol.ucoz.ru (http://aikido-mariupol.ucoz.ru)](http://aikido-mariupol.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://icq-telefon.ucoz.ru) [icq-telefon.ucoz.ru (http://icq-telefon.ucoz.ru)](http://icq-telefon.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://admin-soft.ucoz.ru) [admin-soft.ucoz.ru (http://admin-soft.ucoz.ru)](http://admin-soft.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://kinohouse.ucoz.ru) [kinohouse.ucoz.ru (http://kinohouse.ucoz.ru)](http://kinohouse.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://photoaura.ucoz.ru) [photoaura.ucoz.ru (http://photoaura.ucoz.ru)](http://photoaura.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://mozga-net.ucoz.ru) [mozga-net.ucoz.ru (http://mozga-net.ucoz.ru)](http://mozga-net.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=https://css-nn-52-rus.ucoz.ru) [css-nn-52-rus.ucoz.ru (https://css-nn-52-rus.ucoz.ru)](https://css-nn-52-rus.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://css-nn-52-rus.ucoz.ru) [css-nn-52-rus.ucoz.ru (http://css-nn-52-rus.ucoz.ru)](http://css-nn-52-rus.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://sebastopol.ucoz.ru) [sebastopol.ucoz.ru (http://sebastopol.ucoz.ru)](http://sebastopol.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://remont56.ucoz.ru) [remont56.ucoz.ru (http://remont56.ucoz.ru)](http://remont56.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://compline.ucoz.ru) [compline.ucoz.ru (http://compline.ucoz.ru)](http://compline.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://fs-mods-rus.ucoz.ru) [fs-mods-rus.ucoz.ru (http://fs-mods-rus.ucoz.ru)](http://fs-mods-rus.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=https://zvukinadezdy.ucoz.ru) [zvukinadezdy.ucoz.ru (https://zvukinadezdy.ucoz.ru)](https://zvukinadezdy.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://ps-cs.ucoz.ru) [ps-cs.ucoz.ru (http://ps-cs.ucoz.ru)](http://ps-cs.ucoz.ru)*: top 100M, ru*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=http://zvukinadezdy.ucoz.ru) [zvukinadezdy.ucoz.ru (http://zvukinadezdy.ucoz.ru)](http://zvukinadezdy.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://ps-cs.ucoz.ru) [ps-cs.ucoz.ru (http://ps-cs.ucoz.ru)](http://ps-cs.ucoz.ru)*: top 100M, ru*
1. ![](https://www.google.com/s2/favicons?domain=http://gta-fan-zone.ucoz.ru) [gta-fan-zone.ucoz.ru (http://gta-fan-zone.ucoz.ru)](http://gta-fan-zone.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://scooter-helper.ucoz.ru) [scooter-helper.ucoz.ru (http://scooter-helper.ucoz.ru)](http://scooter-helper.ucoz.ru)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://opinion.ucoz.ru) [opinion.ucoz.ru (http://opinion.ucoz.ru)](http://opinion.ucoz.ru)*: top 100M*
@@ -1754,7 +1756,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://social.tchncs.de/) [social.tchncs.de (https://social.tchncs.de/)](https://social.tchncs.de/)*: top 100M, de*
1. ![](https://www.google.com/s2/favicons?domain=https://forums.alliedmods.net/) [alliedmods (https://forums.alliedmods.net/)](https://forums.alliedmods.net/)*: top 100M, forum, gb, jp, tr, uz*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://forums.gamerevolution.com) [GameRevolution (https://forums.gamerevolution.com)](https://forums.gamerevolution.com)*: top 100M, forum, gaming*
1. ![](https://www.google.com/s2/favicons?domain=https://ru.pathofexile.com) [Pathofexile (https://ru.pathofexile.com)](https://ru.pathofexile.com)*: top 100M, ru*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://ru.pathofexile.com) [Pathofexile (https://ru.pathofexile.com)](https://ru.pathofexile.com)*: top 100M, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://boards.theforce.net) [boards.theforce.net (https://boards.theforce.net)](https://boards.theforce.net)*: top 100M*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://community.justlanded.com) [Justlanded (https://community.justlanded.com)](https://community.justlanded.com)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://forum.igromania.ru/) [igromania (http://forum.igromania.ru/)](http://forum.igromania.ru/)*: top 100M, forum, gaming, ru*
@@ -1826,7 +1828,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://forum.ubuntu-it.org) [forum.ubuntu-it.org (https://forum.ubuntu-it.org)](https://forum.ubuntu-it.org)*: top 100M, ch, forum, it*
1. ![](https://www.google.com/s2/favicons?domain=https://forum.endeavouros.com) [forum.endeavouros.com (https://forum.endeavouros.com)](https://forum.endeavouros.com)*: top 100M, forum*
1. ![](https://www.google.com/s2/favicons?domain=http://forum.newlcn.com) [forum.newlcn.com (http://forum.newlcn.com)](http://forum.newlcn.com)*: top 100M, forum*
1. ![](https://www.google.com/s2/favicons?domain=https://discussion.squadhelp.com) [discussion.squadhelp.com (https://discussion.squadhelp.com)](https://discussion.squadhelp.com)*: top 100M, forum*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://discussion.squadhelp.com) [discussion.squadhelp.com (https://discussion.squadhelp.com)](https://discussion.squadhelp.com)*: top 100M, forum*
1. ![](https://www.google.com/s2/favicons?domain=https://discuss.flarum.org) [discuss.flarum.org (https://discuss.flarum.org)](https://discuss.flarum.org)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=https://forum.mirf.ru/) [mirf (https://forum.mirf.ru/)](https://forum.mirf.ru/)*: top 100M, forum, ru*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=http://kpyto.pp.net.ua) [kpyto.pp.net.ua (http://kpyto.pp.net.ua)](http://kpyto.pp.net.ua)*: top 100M, ua*
@@ -1854,6 +1856,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=http://forums.vkmonline.com) [VKMOnline (http://forums.vkmonline.com)](http://forums.vkmonline.com)*: top 100M, forum, ru*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://are.kamrbb.ru) [AreKamrbb (https://are.kamrbb.ru)](https://are.kamrbb.ru)*: top 100M, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://hyundaitruckclub.kamrbb.ru) [Hyundaitruckclub (https://hyundaitruckclub.kamrbb.ru)](https://hyundaitruckclub.kamrbb.ru)*: top 100M, ru*
1. ![](https://www.google.com/s2/favicons?domain=http://forum.navi.gg/) [navi (http://forum.navi.gg/)](http://forum.navi.gg/)*: top 100M, forum, ru*
1. ![](https://www.google.com/s2/favicons?domain=http://forum.hochu.ua) [hochu (http://forum.hochu.ua)](http://forum.hochu.ua)*: top 100M, forum, ru, ua*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=http://forum.kineshemec.ru) [forum.kineshemec.ru (http://forum.kineshemec.ru)](http://forum.kineshemec.ru)*: top 100M, forum, ru*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://community.p2pu.org) [community.p2pu.org (https://community.p2pu.org)](https://community.p2pu.org)*: top 100M, forum*
@@ -1883,9 +1886,9 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://forum.antichat.ru/) [Antichat (https://forum.antichat.ru/)](https://forum.antichat.ru/)*: top 100M, forum, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://id.dev.by) [Dev.by (https://id.dev.by)](https://id.dev.by)*: top 100M, by, news, tech*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://forum.gong.bg) [forum.gong.bg (https://forum.gong.bg)](https://forum.gong.bg)*: top 100M, bg, forum*
1. ![](https://www.google.com/s2/favicons?domain=https://forum.velomania.ru/) [Velomania (https://forum.velomania.ru/)](https://forum.velomania.ru/)*: top 100M, forum, ru*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://forum.velomania.ru/) [Velomania (https://forum.velomania.ru/)](https://forum.velomania.ru/)*: top 100M, forum, ru*
1. ![](https://www.google.com/s2/favicons?domain=http://bbs.evony.com) [bbs.evony.com (http://bbs.evony.com)](http://bbs.evony.com)*: top 100M, forum, pk, tr*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://forum.vectric.com) [forum.vectric.com (https://forum.vectric.com)](https://forum.vectric.com)*: top 100M, forum*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://forum.vectric.com) [forum.vectric.com (https://forum.vectric.com)](https://forum.vectric.com)*: top 100M, forum*
1. ![](https://www.google.com/s2/favicons?domain=http://forum.bratsk.org) [Bratsk Forum (http://forum.bratsk.org)](http://forum.bratsk.org)*: top 100M, forum, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://forums.runnersworld.co.uk/) [Runnersworld (https://forums.runnersworld.co.uk/)](https://forums.runnersworld.co.uk/)*: top 100M, forum, sport*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=http://forum.qwas.ru) [Qwas (http://forum.qwas.ru)](http://forum.qwas.ru)*: top 100M, forum, ru*
@@ -2103,7 +2106,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=http://eightbit.me/) [Eightbit (http://eightbit.me/)](http://eightbit.me/)*: top 100M*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=) [Elftown ()]()*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=https://elwo.ru) [Elwo (https://elwo.ru)](https://elwo.ru)*: top 100M, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://www.engadget.com/) [Engadget (https://www.engadget.com/)](https://www.engadget.com/)*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=) [Engadget ()]()*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=https://enot-poloskun.ru/) [Enot-poloskun (https://enot-poloskun.ru/)](https://enot-poloskun.ru/)*: top 100M, ru*
1. ![](https://www.google.com/s2/favicons?domain=) [Eporner ()]()*: top 100M, es*
1. ![](https://www.google.com/s2/favicons?domain=https://erboh.com/) [Erboh (https://erboh.com/)](https://erboh.com/)*: top 100M, forum, pk*, search is disabled
@@ -2141,7 +2144,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://fireworktv.com) [Fireworktv (https://fireworktv.com)](https://fireworktv.com)*: top 100M, jp*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://flbord.com) [Flbord (https://flbord.com)](https://flbord.com)*: top 100M, ru, ua*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://fm-forum.ru) [Fm-forum (https://fm-forum.ru)](https://fm-forum.ru)*: top 100M, forum, ru*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=http://forum.glow-dm.ru) [Forum.glow-dm.ru (http://forum.glow-dm.ru)](http://forum.glow-dm.ru)*: top 100M, forum, ru*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=http://forum.glow-dm.ru) [Forum.glow-dm.ru (http://forum.glow-dm.ru)](http://forum.glow-dm.ru)*: top 100M, forum, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://forum.jambox.ru) [Forum.jambox.ru (https://forum.jambox.ru)](https://forum.jambox.ru)*: top 100M, forum, ru*
1. ![](https://www.google.com/s2/favicons?domain=http://forum.quake2.com.ru/) [Forum.quake2.com.ru (http://forum.quake2.com.ru/)](http://forum.quake2.com.ru/)*: top 100M, forum, ru*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=http://forum29.net) [Forum29 (http://forum29.net)](http://forum29.net)*: top 100M, forum, ru*, search is disabled
@@ -2199,7 +2202,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://www.invalidnost.com) [Invalidnost (https://www.invalidnost.com)](https://www.invalidnost.com)*: top 100M, ru*
1. ![](https://www.google.com/s2/favicons?domain=) [IonicFramework ()]()*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://ispdn.ru) [Ispdn (http://ispdn.ru)](http://ispdn.ru)*: top 100M, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://itforums.ru) [Itforums (https://itforums.ru)](https://itforums.ru)*: top 100M, forum, ru*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://itforums.ru) [Itforums (https://itforums.ru)](https://itforums.ru)*: top 100M, forum, ru*
1. ![](https://www.google.com/s2/favicons?domain=https://itfy.org) [Itfy (https://itfy.org)](https://itfy.org)*: top 100M, ru*
1. ![](https://www.google.com/s2/favicons?domain=) [Jbzd ()]()*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=) [Jeja.pl ()]()*: top 100M*
@@ -2278,7 +2281,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=) [Ninjakiwi ()]()*: top 100M*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://www.nationalgunforum.com) [NationalgunForum (https://www.nationalgunforum.com)](https://www.nationalgunforum.com)*: top 100M, ca, forum*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://naturalworld.guru) [Naturalworld (https://naturalworld.guru)](https://naturalworld.guru)*: top 100M, ru*
1. ![](https://www.google.com/s2/favicons?domain=) [Needrom ()]()*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=) [Needrom ()]()*: top 100M*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://no-jus.com) [No-jus (https://no-jus.com)](https://no-jus.com)*: top 100M, ru*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=https://numizmat-forum.ru) [Numizmat (https://numizmat-forum.ru)](https://numizmat-forum.ru)*: top 100M, forum, ru*
1. ![](https://www.google.com/s2/favicons?domain=) [Nyaa.si ()]()*: top 100M*
@@ -2306,7 +2309,7 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=) [Polczat.pl ()]()*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=) [Policja2009 ()]()*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=) [Polleverywhere ()]()*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=) [Polymart ()]()*: top 100M*, search is disabled
1. ![](https://www.google.com/s2/favicons?domain=) [Polymart ()]()*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=) [PornhubPornstars ()]()*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=) [Poshmark ()]()*: top 100M*
1. ![](https://www.google.com/s2/favicons?domain=http://pro-cats.ru) [Pro-cats (http://pro-cats.ru)](http://pro-cats.ru)*: top 100M, ru*
@@ -3158,18 +3161,18 @@ Rank data fetched from Majestic Million by domains.
1. ![](https://www.google.com/s2/favicons?domain=https://app.airnfts.com) [AirNFTs (https://app.airnfts.com)](https://app.airnfts.com)*: top 100M, crypto, nft*
1. ![](https://www.google.com/s2/favicons?domain=https://greasyfork.org) [GreasyFork (https://greasyfork.org)](https://greasyfork.org)*: top 100M, coding*
The list was updated at (2026-05-09)
The list was updated at (2026-04-29)
## Statistics
Enabled/total sites: 2524/3154 = 80.03%
Enabled/total sites: 2523/3157 = 79.92%
Incomplete message checks: 311/2524 = 12.32% (false positive risks)
Incomplete message checks: 316/2523 = 12.52% (false positive risks)
Status code checks: 636/2524 = 25.2% (false positive risks)
Status code checks: 633/2523 = 25.09% (false positive risks)
False positive risk (total): 37.52%
False positive risk (total): 37.61%
Sites with probing: 500px, Armchairgm, BinarySearch (disabled), BleachFandom, Bluesky, BongaCams, Boosty, BuyMeACoffee, Calendly, Cent, Chess, Code Sandbox (disabled), Code Snippet Wiki, DailyMotion, Discord, Diskusjon.no, Disqus, Docker Hub, Duolingo, FandomCommunityCentral, GitHub, GitLab, Google Plus (archived), Gravatar, HackTheBox, Hackerrank, Hashnode, Holopin, Imgur, Issuu, Keybase, Kick, Kvinneguiden, LeetCode, Lesswrong, Livejasmin, LocalCryptos (disabled), Medium, MicrosoftLearn, MixCloud, Monkeytype, NPM, Niftygateway, Omg.lol, OnlyFans, Paragraph, Picsart, Plurk, Polarsteps, Rarible, Reddit, Reddit Search (Pushshift) (disabled), Revolut.me, RoyalCams, Scratch, Soop, SportsTracker, Spotify, StackOverflow, Substack, TAP'D, Topcoder, Trello, Twitch, Twitter, Twitter Shadowban (disabled), UnstoppableDomains, Vimeo, Vivino, Warframe Market, Warpcast, Weibo, Wikipedia, Yapisal (disabled), YouNow, en.brickimedia.org, forums.grandstream.com, nightbot, notabug.org, qiwi.me (disabled)
Sites with probing: 500px, Armchairgm, BinarySearch (disabled), BleachFandom, Bluesky, BongaCams, Boosty, BuyMeACoffee, Calendly, Cent, Chess, Code Sandbox, Code Snippet Wiki, DailyMotion, Discord, Diskusjon.no, Disqus, Docker Hub, Duolingo, FandomCommunityCentral, GitHub, GitLab, Google Plus (archived), Gravatar, HackTheBox, Hackerrank, Hashnode, Holopin, Imgur, Issuu, Keybase, Kick, Kvinneguiden, LeetCode, Lesswrong, Livejasmin, LocalCryptos (disabled), Medium, MicrosoftLearn, MixCloud, Monkeytype, NPM, Niftygateway, Omg.lol, OnlyFans, Paragraph, Picsart, Plurk, Polarsteps, Rarible, Reddit (disabled), Reddit Search (Pushshift) (disabled), Revolut.me, RoyalCams, Scratch, Soop, SportsTracker, Spotify, StackOverflow, Substack, TAP'D, Topcoder, Trello, Twitch, Twitter, Twitter Shadowban (disabled), UnstoppableDomains, Vimeo, Vivino, Warframe Market, Warpcast, Weibo, Wikipedia, Yapisal (disabled), YouNow, en.brickimedia.org, forums.grandstream.com, nightbot, notabug.org, qiwi.me (disabled)
Sites with activation: OnlyFans, Twitter, Vimeo, Weibo
@@ -3177,8 +3180,8 @@ Top 20 profile URLs:
- (709) `{urlMain}/index/8-0-{username} (uCoz)`
- (312) `/{username}`
- (223) `{urlMain}{urlSubpath}/members/?username={username} (XenForo)`
- (171) `/user/{username}`
- (139) `/profile/{username}`
- (172) `/user/{username}`
- (140) `/profile/{username}`
- (127) `{urlMain}{urlSubpath}/search.php?author={username} (phpBB/Search)`
- (120) `{urlMain}{urlSubpath}/member.php?username={username} (vBulletin)`
- (116) `/u/{username}`
@@ -3188,7 +3191,7 @@ Top 20 profile URLs:
- (55) `/wiki/User:{username}`
- (45) `SUBDOMAIN`
- (38) `/members/?username={username}`
- (32) `/author/{username}`
- (31) `/author/{username}`
- (30) `/members/{username}`
- (27) `{urlMain}{urlSubpath}/memberlist.php?username={username} (phpBB)`
- (18) `/forum/search.php?keywords=&terms=all&author={username}`
@@ -3198,10 +3201,10 @@ Top 20 profile URLs:
Sites by engine:
- `uCoz`: 634/709 (89.4%)
- `XenForo`: 177/223 (79.4%)
- `phpBB/Search`: 119/127 (93.7%)
- `XenForo`: 181/223 (81.2%)
- `phpBB/Search`: 120/127 (94.5%)
- `vBulletin`: 31/120 (25.8%)
- `Discourse`: 84/92 (91.3%)
- `Discourse`: 86/92 (93.5%)
- `phpBB`: 21/27 (77.8%)
- `engine404`: 19/23 (82.6%)
- `op.gg`: 17/17 (100.0%)
@@ -3214,10 +3217,10 @@ Sites by engine:
Top 20 tags:
- (1057) `NO_TAGS` (non-standard)
- (749) `forum`
- (750) `forum`
- (128) `gaming`
- (88) `coding`
- (57) `photo`
- (58) `photo`
- (46) `tech`
- (45) `social`
- (42) `news`
-10
View File
@@ -309,16 +309,6 @@ def test_process_site_result_with_error_is_unknown():
assert out["status"].error is not None
def test_process_site_result_error_context_uses_instance():
# Regression: context must render the CheckError instance, not the class.
site = _make_site({"checkType": "status_code"})
info = {"username": "a", "parsing_enabled": False, "url_user": "https://x/a"}
err = CheckError("Request timeout", "slow server")
out = process_site_result(("body", 0, err), Mock(), Mock(), info, site)
assert out["status"].context == "Request timeout error: slow server"
assert "class" not in out["status"].context
# ---- CurlCffiChecker: TLS impersonation header sanitisation ----
-1
View File
@@ -53,7 +53,6 @@ DEFAULT_ARGS: Dict[str, Any] = {
'ai_model': 'gpt-4o',
'no_autoupdate': False,
'force_update': False,
'cloudflare_bypass': False,
}
-256
View File
@@ -1,256 +0,0 @@
"""Tests for the Cloudflare webgate config + checker."""
import json
from types import SimpleNamespace
from mock import Mock
import pytest
from maigret.checking import (
CloudflareWebgateChecker,
build_cloudflare_bypass_config,
)
def _settings(payload):
return SimpleNamespace(cloudflare_bypass=payload)
def test_config_disabled_by_default():
s = _settings({"enabled": False, "modules": [{"method": "json_api", "url": "x"}]})
assert build_cloudflare_bypass_config(s, force_enable=False) is None
def test_config_force_enable_overrides_disabled_settings():
s = _settings({"enabled": False, "modules": [{"method": "json_api", "url": "http://x:8191/v1"}]})
cfg = build_cloudflare_bypass_config(s, force_enable=True)
assert cfg is not None
assert cfg["modules"][0]["url"] == "http://x:8191/v1"
def test_config_drops_invalid_modules():
s = _settings({
"enabled": True,
"modules": [
{"method": "url_rewrite", "url": "http://x:8000/html"}, # missing {url}
{"method": "json_api", "url": "http://x:8191/v1"},
{"method": "unknown", "url": "http://x"},
],
})
cfg = build_cloudflare_bypass_config(s)
assert len(cfg["modules"]) == 1
assert cfg["modules"][0]["method"] == "json_api"
def test_config_returns_none_when_no_valid_modules():
s = _settings({"enabled": True, "modules": [{"method": "url_rewrite", "url": "no-placeholder"}]})
assert build_cloudflare_bypass_config(s) is None
def test_config_default_trigger_protection():
s = _settings({"enabled": True, "modules": [{"method": "json_api", "url": "http://x:8191/v1"}]})
cfg = build_cloudflare_bypass_config(s)
assert "cf_js_challenge" in cfg["trigger_protection"]
assert "cf_firewall" in cfg["trigger_protection"]
assert "webgate" in cfg["trigger_protection"]
@pytest.mark.asyncio
async def test_flaresolverr_success(httpserver):
httpserver.expect_request("/v1", method="POST").respond_with_json({
"status": "ok",
"solution": {"status": 404, "response": "<html>missing</html>", "url": "https://site/missing"},
})
config = {
"modules": [{"name": "fs", "method": "json_api", "url": httpserver.url_for("/v1")}],
"session_prefix": "test",
}
c = CloudflareWebgateChecker(logger=Mock(), config=config)
c.prepare(url="https://site/missing", timeout=5)
body, status, err = await c.check()
assert err is None
assert status == 404 # upstream status preserved — fixes status_code checktype
assert "missing" in body
@pytest.mark.asyncio
async def test_flaresolverr_solver_error_propagates(httpserver):
httpserver.expect_request("/v1", method="POST").respond_with_json({
"status": "error",
"message": "Challenge could not be solved",
})
config = {
"modules": [{"name": "fs", "method": "json_api", "url": httpserver.url_for("/v1")}],
}
c = CloudflareWebgateChecker(logger=Mock(), config=config)
c.prepare(url="https://site/page", timeout=5)
body, status, err = await c.check()
assert err is not None
assert "Challenge could not be solved" in err.desc
@pytest.mark.asyncio
async def test_falls_back_to_next_module_on_failure(httpserver):
# Bind only the second module — the first is unreachable.
httpserver.expect_request("/v1", method="POST").respond_with_json({
"status": "ok",
"solution": {"status": 200, "response": "ok-from-second", "url": "https://x"},
})
config = {
"modules": [
{"name": "broken", "method": "json_api", "url": "http://127.0.0.1:1/v1"},
{"name": "good", "method": "json_api", "url": httpserver.url_for("/v1")},
],
}
c = CloudflareWebgateChecker(logger=Mock(), config=config)
c.prepare(url="https://site/page", timeout=5)
body, status, err = await c.check()
assert err is None
assert status == 200
assert body == "ok-from-second"
@pytest.mark.asyncio
async def test_url_rewrite_returns_html_with_synthetic_200(httpserver):
# CloudflareBypassForScraping returns just the rendered HTML, no JSON wrapper.
httpserver.expect_request("/html").respond_with_data(
"<html>profile body</html>", status=200, content_type="text/html"
)
config = {
"modules": [{
"name": "cbfs",
"method": "url_rewrite",
"url": httpserver.url_for("/html") + "?url={url}",
}],
}
c = CloudflareWebgateChecker(logger=Mock(), config=config)
c.prepare(url="https://site/page", timeout=5)
body, status, err = await c.check()
assert err is None
assert status == 200 # synthetic — url_rewrite cannot recover real status
assert "profile body" in body
@pytest.mark.asyncio
async def test_all_modules_unreachable_actionable_error():
config = {
"modules": [
{"name": "fs", "method": "json_api", "url": "http://127.0.0.1:1/v1"},
{"name": "cbfs", "method": "url_rewrite", "url": "http://127.0.0.1:2/html?url={url}"},
],
}
c = CloudflareWebgateChecker(logger=Mock(), config=config)
c.prepare(url="https://site/page", timeout=2)
body, status, err = await c.check()
assert err is not None
assert err.type == "Webgate unavailable"
# Per-module attempt summary helps users see WHICH backend failed
assert "fs:" in err.desc and "cbfs:" in err.desc
# Primary URL is shown so the user knows where to look
assert "http://127.0.0.1:1/v1" in err.desc
# FlareSolverr docker hint when primary is json_api
assert "flaresolverr" in err.desc.lower()
@pytest.mark.asyncio
async def test_session_is_scoped_per_host(httpserver):
seen_sessions = []
def handler(request):
seen_sessions.append(request.get_json()["session"])
return {"status": "ok", "solution": {"status": 200, "response": "", "url": "x"}}
httpserver.expect_request("/v1", method="POST").respond_with_handler(handler)
config = {"modules": [{"name": "fs", "method": "json_api", "url": httpserver.url_for("/v1")}]}
c = CloudflareWebgateChecker(logger=Mock(), config=config)
c.prepare(url="https://patreon.com/foo", timeout=5)
await c.check()
c.prepare(url="https://patreon.com/bar", timeout=5)
await c.check()
c.prepare(url="https://lomography.com/baz", timeout=5)
await c.check()
assert seen_sessions[0] == seen_sessions[1], "same host -> same session"
assert seen_sessions[0] != seen_sessions[2], "different host -> different session"
assert "patreon.com" in seen_sessions[0]
assert "lomography.com" in seen_sessions[2]
@pytest.mark.asyncio
async def test_flaresolverr_request_body_shape(httpserver):
captured = {}
def handler(request):
captured["body"] = request.get_json()
return {"status": "ok", "solution": {"status": 200, "response": "", "url": "x"}}
httpserver.expect_request("/v1", method="POST").respond_with_handler(handler)
config = {"modules": [{"name": "fs", "method": "json_api", "url": httpserver.url_for("/v1")}]}
c = CloudflareWebgateChecker(logger=Mock(), config=config)
c.prepare(url="https://site/page", headers={"User-Agent": "test-ua/1.0"}, timeout=5)
await c.check()
body = captured["body"]
assert body["cmd"] == "request.get"
assert body["url"] == "https://site/page"
assert body["session"].startswith("maigret-")
# userAgent was removed in FlareSolverr v2; the impersonated browser's
# own UA must be used to keep TLS+UA consistent.
assert "userAgent" not in body
assert "proxy" not in body
@pytest.mark.asyncio
async def test_flaresolverr_proxy_string_passed_through(httpserver):
captured = {}
def handler(request):
captured["body"] = request.get_json()
return {"status": "ok", "solution": {"status": 200, "response": "", "url": "x"}}
httpserver.expect_request("/v1", method="POST").respond_with_handler(handler)
config = {
"modules": [
{
"name": "fs",
"method": "json_api",
"url": httpserver.url_for("/v1"),
"proxy": "socks5://localhost:1080",
}
]
}
c = CloudflareWebgateChecker(logger=Mock(), config=config)
c.prepare(url="https://site/page", headers={}, timeout=5)
await c.check()
assert captured["body"]["proxy"] == {"url": "socks5://localhost:1080"}
@pytest.mark.asyncio
async def test_flaresolverr_proxy_dict_with_credentials(httpserver):
captured = {}
def handler(request):
captured["body"] = request.get_json()
return {"status": "ok", "solution": {"status": 200, "response": "", "url": "x"}}
httpserver.expect_request("/v1", method="POST").respond_with_handler(handler)
config = {
"modules": [
{
"name": "fs",
"method": "json_api",
"url": httpserver.url_for("/v1"),
"proxy": {
"url": "http://proxy.example:3128",
"username": "u",
"password": "p",
"stripped_extra": "ignored",
},
}
]
}
c = CloudflareWebgateChecker(logger=Mock(), config=config)
c.prepare(url="https://site/page", headers={}, timeout=5)
await c.check()
proxy = captured["body"]["proxy"]
assert proxy == {"url": "http://proxy.example:3128", "username": "u", "password": "p"}
-18
View File
@@ -1,7 +1,5 @@
"""Maigret Database test functions"""
import re
from typing import Any, Dict
from maigret.sites import MaigretDatabase, MaigretSite
@@ -128,22 +126,6 @@ def test_site_url_detector():
)
def test_extract_id_from_url_skips_none_groups():
site = MaigretSite(
"Example",
{
"urlMain": "https://example.com",
"url": "https://example.com/{username}",
},
)
site.url_regexp = re.compile(r"^https://example\.com/([^/?#]+)(?:/(.*))?$")
assert site.extract_id_from_url("https://example.com/username") == (
"username",
"username",
)
def test_ranked_sites_dict():
db = MaigretDatabase()
db.update_site(MaigretSite('3', {'alexaRank': 1000, 'engine': 'ucoz'}))
-172
View File
@@ -1,172 +0,0 @@
"""Smoke tests for the Flask web interface in maigret.web.app.
The goal is to catch breakage in the basic user flow (render index, kick off
search, redirect to results) without making real network calls. Heavy maigret
internals are mocked; the report-generation smoke test keeps `save_graph_report`
unmocked so regressions like `nt.options.groups = ...` (AttributeError on a
plain dict) are caught automatically.
"""
import os
import pytest
import maigret
import maigret.report
from maigret.web import app as web_app_module
CUR_PATH = os.path.dirname(os.path.realpath(__file__))
TEST_DB = os.path.join(CUR_PATH, 'db.json')
class _SyncThread:
"""Drop-in for threading.Thread that runs target synchronously on start()."""
def __init__(self, target=None, args=(), kwargs=None, **_):
self._target = target
self._args = args
self._kwargs = kwargs or {}
def start(self):
self._target(*self._args, **self._kwargs)
@pytest.fixture
def web_app(tmp_path):
web_app_module.app.config['TESTING'] = True
web_app_module.app.config['REPORTS_FOLDER'] = str(tmp_path)
web_app_module.app.config['MAIGRET_DB_FILE'] = TEST_DB
web_app_module.background_jobs.clear()
web_app_module.job_results.clear()
yield web_app_module
web_app_module.background_jobs.clear()
web_app_module.job_results.clear()
@pytest.fixture
def client(web_app):
return web_app.app.test_client()
def test_index_renders(client):
resp = client.get('/')
assert resp.status_code == 200
body = resp.get_data(as_text=True)
assert 'name="usernames"' in body
assert '<form' in body
def test_search_empty_input_redirects_to_index(client):
resp = client.post('/search', data={'usernames': ''})
assert resp.status_code == 302
assert resp.location.rstrip('/').endswith('') or resp.location.endswith('/')
def test_search_redirects_to_status(client, web_app, monkeypatch):
monkeypatch.setattr(web_app, 'process_search_task', lambda *a, **kw: None)
monkeypatch.setattr(web_app, 'Thread', _SyncThread)
resp = client.post('/search', data={'usernames': 'soxoj'})
assert resp.status_code == 302
assert '/status/' in resp.location
def test_invalid_timestamp_redirects_to_index(client):
resp = client.get('/status/nonexistent_ts')
assert resp.status_code == 302
assert resp.location.endswith('/')
def test_status_running_renders_status_page(client, web_app, monkeypatch):
"""While the background job is still running, /status/<ts> returns 200."""
def never_completes(usernames, options, timestamp):
# leave background_jobs[timestamp]['completed'] as False
pass
monkeypatch.setattr(web_app, 'process_search_task', never_completes)
monkeypatch.setattr(web_app, 'Thread', _SyncThread)
post = client.post('/search', data={'usernames': 'soxoj'})
status_resp = client.get(post.location)
assert status_resp.status_code == 200
def test_completed_search_redirects_to_results(client, web_app, monkeypatch):
"""Happy path: POST /search → background completes → /status/<ts> → /results/<session>."""
def fake_task(usernames, options, timestamp):
web_app.job_results[timestamp] = {
'status': 'completed',
'session_folder': f'search_{timestamp}',
'graph_file': f'search_{timestamp}/combined_graph.html',
'usernames': usernames,
'individual_reports': [],
}
web_app.background_jobs[timestamp]['completed'] = True
monkeypatch.setattr(web_app, 'process_search_task', fake_task)
monkeypatch.setattr(web_app, 'Thread', _SyncThread)
post = client.post('/search', data={'usernames': 'soxoj'})
assert post.status_code == 302
status_resp = client.get(post.location)
assert status_resp.status_code == 302
assert '/results/search_' in status_resp.location
results_resp = client.get(status_resp.location)
assert results_resp.status_code == 200
assert b'soxoj' in results_resp.data
def test_failed_task_redirects_to_index(client, web_app, monkeypatch):
def failing_task(usernames, options, timestamp):
web_app.job_results[timestamp] = {'status': 'failed', 'error': 'boom'}
web_app.background_jobs[timestamp]['completed'] = True
monkeypatch.setattr(web_app, 'process_search_task', failing_task)
monkeypatch.setattr(web_app, 'Thread', _SyncThread)
post = client.post('/search', data={'usernames': 'soxoj'})
status_resp = client.get(post.location)
assert status_resp.status_code == 302
assert status_resp.location.endswith('/')
def test_real_report_generation_does_not_crash(client, web_app, monkeypatch):
"""End-to-end with mocked maigret.search but REAL report generation.
This is the regression guard for bugs inside `save_graph_report` and friends
(e.g. `nt.options.groups = ...` raising AttributeError on a dict). If any of
the unmocked report functions throws, the task records a failed status and
this assertion catches it.
"""
async def fake_search(*args, **kwargs):
return {}
monkeypatch.setattr(maigret, 'search', fake_search)
# Mock the per-username report writers — they are not what we care about here,
# and pdf/html generation pulls in xhtml2pdf which is slow and brittle.
monkeypatch.setattr(maigret.report, 'save_csv_report', lambda *a, **kw: None)
monkeypatch.setattr(maigret.report, 'save_json_report', lambda *a, **kw: None)
monkeypatch.setattr(maigret.report, 'save_pdf_report', lambda *a, **kw: None)
monkeypatch.setattr(maigret.report, 'save_html_report', lambda *a, **kw: None)
monkeypatch.setattr(maigret.report, 'generate_report_context', lambda *a, **kw: {})
monkeypatch.setattr(web_app, 'Thread', _SyncThread)
post = client.post('/search', data={'usernames': 'testuser'})
timestamp = post.location.rsplit('/', 1)[1]
assert timestamp in web_app.job_results, 'background task did not record any result'
result = web_app.job_results[timestamp]
assert result['status'] == 'completed', (
f"report generation failed: {result.get('error')!r}"
)