From 80f8cc667a4f4a1bb417da6a4390ed9602a642f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A4=D0=BE=D1=84=D0=B0=D0=BD=D0=BE=D0=B2=20=D0=94=D0=BC?= =?UTF-8?q?=D0=B8=D1=82=D1=80=D0=B8=D0=B9=20=D0=92=D0=B8=D0=BA=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B8=D1=87?= Date: Tue, 1 Sep 2026 11:39:26 +0300 Subject: [PATCH] Document public release and narrow tracker scope --- LICENSE | 21 ++++++++ README.md | 80 +++++++++++++++++++++++++++--- src/torrent_tracker_scraper/udp.py | 2 + 3 files changed, 96 insertions(+), 7 deletions(-) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..254ce26 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Dmitry Fofanov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 956c240..8d6c39c 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,79 @@ # Torrent Tracker Scraper -Современная, компактная переработка `/_torrent/torrent.py` и `GetPeers.py`. -Утилита отправляет стандартный UDP scrape-запрос трекеру и выводит число seeders, -completed и leechers для указанного info hash. Она не скачивает файлы и не -подключается к пиринговой сети. +Минимальная утилита для чтения агрегированной статистики одного info hash через +UDP BitTorrent tracker protocol (BEP 15). Проект переработан из `_torrent/torrent.py` +и `GetPeers.py`: парсинг сайтов, прокси, базы данных, загрузка контента и прочий +несвязанный код удалены. Остался только запрос состояния трекера. + +## Что делает + +Программа выполняет два стандартных UDP-запроса к указанному трекеру: + +1. `connect` — получает временный connection ID; +2. `scrape` — получает `seeders`, `completed` и `leechers` для одного info hash. + +Она не скачивает файлы, не отправляет announce, не присоединяется к swarm, не +обходит ограничения и не записывает состояние на диск. Ответ зависит от политики +конкретного трекера: некоторые отключают scrape или требуют аутентификацию. + +## Установка + +Требуется Python 3.10 или новее. Внешних Python-зависимостей нет. ```bash -python -m pip install -e . -torrent-tracker-scrape tracker.example.org 6969 0123456789abcdef0123456789abcdef01234567 +git clone https://github.dfv24.com/fofanov.dmitry/torrent-tracker-scraper.git +cd torrent-tracker-scraper +python -m venv .venv +. .venv/bin/activate +python -m pip install --editable . ``` -Используйте только с трекерами и материалами, доступ к которым у вас есть. +## Использование + +```bash +torrent-tracker-scrape tracker.example.org 6969 \ + 0123456789abcdef0123456789abcdef01234567 +``` + +Параметры: + +- `host` — DNS-имя или IPv4-адрес UDP-трекера; +- `port` — UDP-порт трекера; +- `info_hash` — ровно 40 шестнадцатеричных символов; +- `--timeout 5` — необязательное время ожидания в секундах, по умолчанию `8`. + +Успешный ответ имеет вид: + +```text +seeders=12 completed=340 leechers=4 +``` + +При недоступности трекера, неожиданном ответе или неверном hash программа +завершается с ошибкой. Это намеренно: вызывающий скрипт не должен принять +неполные сетевые данные за нулевую статистику. + +## Структура и проверка + +```text +src/torrent_tracker_scraper/ + udp.py # реализация пакетов BEP 15 и проверка ответов + cli.py # параметры команды и текстовый вывод +``` + +```bash +python -m compileall -q src +PYTHONPATH=src python -c "from torrent_tracker_scraper.udp import scrape; scrape('127.0.0.1', 1, 'bad')" +``` + +Вторая команда должна завершиться с понятной ошибкой валидации hash до сетевого +обращения. + +## Использование по назначению + +Работайте только с трекерами и материалами, к которым у вас есть законный доступ. +Проверьте правила трекера до отправки запросов: публичность адреса не означает, +что статистику разрешено опрашивать автоматически. + +## Лицензия + +Код распространяется по лицензии [MIT](LICENSE). diff --git a/src/torrent_tracker_scraper/udp.py b/src/torrent_tracker_scraper/udp.py index 9df4b48..6488e6f 100644 --- a/src/torrent_tracker_scraper/udp.py +++ b/src/torrent_tracker_scraper/udp.py @@ -51,11 +51,13 @@ def scrape(host: str, port: int, info_hash: str, timeout: float = 8.0) -> Scrape address = (socket.gethostbyname(host), port) with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock: sock.settimeout(timeout) + # Первый пакет устанавливает короткоживущую сессию с UDP-трекером. connect_tx = _transaction_id() sock.sendto(struct.pack("!QII", _PROTOCOL_ID, 0, connect_tx), address) connect_response = _receive(sock, connect_tx, 16) connection_id = struct.unpack_from("!Q", connect_response, 8)[0] + # Второй пакет читает только агрегированную статистику по одному hash. scrape_tx = _transaction_id() sock.sendto(struct.pack("!QII20s", connection_id, 2, scrape_tx, encoded_hash), address) response = _receive(sock, scrape_tx, 20)