Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
2982cff
Bump h11 from 0.14.0 to 0.16.0
dependabot[bot] Apr 24, 2025
53c1da9
Merge branch 'dev' into dependabot/pip/h11-0.16.0
KingAkeem Jul 19, 2026
833b72d
Merge pull request #354 from DedSecInside/dependabot/pip/h11-0.16.0
KingAkeem Jul 19, 2026
1d8be41
Bump h11 from 0.14.0 to 0.16.0
dependabot[bot] Jul 19, 2026
fff1d35
Merge pull request #396 from DedSecInside/dependabot/pip/h11-0.16.0
KingAkeem Jul 19, 2026
a9eb608
Potential fix for code scanning alert no. 1: Workflow does not contai…
KingAkeem Jul 19, 2026
d90b6ef
Merge pull request #397 from DedSecInside/alert-autofix-1
KingAkeem Jul 19, 2026
5a29136
Fix httpcore compatibility with h11 0.16
KingAkeem Jul 19, 2026
b8266f7
Merge pull request #398 from DedSecInside/fix/httpcore-h11-ci
KingAkeem Jul 19, 2026
bf618de
Bump setuptools from 78.1.1 to 83.0.0
dependabot[bot] Jul 24, 2026
af3efc7
Merge pull request #399 from DedSecInside/dependabot/pip/setuptools-8…
KingAkeem Jul 28, 2026
4735419
Add optional TorBotApp launch from the CLI
KingAkeem Jul 28, 2026
a5a9c81
Prompt for PyPI token when publishing
KingAkeem Jul 28, 2026
8e7ae90
Polish the 4.2.0 changelog
KingAkeem Jul 28, 2026
87c9576
Merge pull request #400 from DedSecInside/feature/optional-torbot-app
KingAkeem Jul 28, 2026
b9b749c
Make NLP classification cheaper during crawls
KingAkeem Jul 28, 2026
d4c5b1c
Bump version for the NLP cleanup
KingAkeem Jul 28, 2026
4863c44
Merge pull request #401 from DedSecInside/improve/nlp-classifier-runtime
KingAkeem Jul 28, 2026
527872e
Make the publish script upload by default
KingAkeem Jul 28, 2026
9a829a5
Merge pull request #402 from DedSecInside/fix/publish-script-uploads
KingAkeem Jul 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/flake8.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ name: Flake8

on: [push]

permissions:
contents: read

jobs:
build:
runs-on: ubuntu-latest
Expand Down
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
### Recent improvements
- The CLI now allows `--version` and `--update` to run without requiring a URL.
- The crawler now resolves relative links against the current page and skips unsupported or malformed hrefs more gracefully.
- The optional TorBotApp desktop UI can be launched from the CLI with `torbot app` when installed as a sibling checkout.

...(will be updated)

Expand Down Expand Up @@ -78,6 +79,9 @@ python main.py -u https://example.com --depth 2 --save json

# Disable SOCKS5 if you are not using Tor locally
python main.py -u https://example.com --disable-socks5 --visualize tree

# Launch the optional desktop app when TorBotApp is installed
torbot app
```

### Options
Expand All @@ -93,6 +97,8 @@ optional arguments:
-v Displays DEBUG level logging, default is INFO
--version Show the current version of TorBot.
--update Update TorBot to the latest stable version
--app Run optional TorBot desktop app
--app-dir APP_DIR Path to a TorBotApp checkout
-q, --quiet Prevents display of header and IP address
--save FORMAT Save results in a file. (tree, JSON)
--visualize FORMAT Visualizes tree of data gathered. (tree, JSON, table)
Expand All @@ -106,6 +112,36 @@ If you are using Tor locally, keep the SOCKS5 proxy enabled. If you are not usin

Read more about torrc here: [Torrc](https://github.com/DedSecInside/TorBoT/blob/master/Tor.md)

## Optional desktop app

TorBot remains a CLI-first Python tool. The desktop UI lives in the separate
[TorBotApp](https://github.com/KingAkeem/TorBotApp) Electron project so Node and
Electron are not required for CLI users.

To launch it from this CLI, install TorBotApp as a sibling checkout:

```text
code/
+-- TorBot/
+-- TorBotApp/
```

Then run:

```sh
torbot app
```

If TorBotApp is somewhere else, use either:

```sh
TORBOT_APP_DIR=/path/to/TorBotApp torbot app
torbot --app --app-dir /path/to/TorBotApp
```

See [Desktop App Architecture](docs/DESKTOP_APP.md) for how the CLI, optional UI,
and backend services fit together.

## Curated Features
- [x] Visualization Module Revamp
- [x] Implement BFS Search for webcrawler
Expand Down
31 changes: 31 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,37 @@
--------------------
All notable changes to this project will be documented in this file.

## 4.3.0 - 2026-07-28

### Changed
- Made NLP classification train lazily from the packaged CSV and cache the classifier for the process, avoiding model retraining for every crawled page.
- Changed the NLP score returned by `classify()` from a misleading random-split accuracy value to prediction confidence for the selected category.

### Fixed
- Removed runtime `training_data/` generation from page classification so installed packages do not try to write generated files into `site-packages`.
- Removed process-wide directory changes from the NLP helpers.

### Tests
- Added direct NLP coverage for HTML text extraction, CSV loading, classifier scoring, and path-safe training-data export.

## 4.2.0 - 2026-07-28

Install from PyPI after publishing with:

```sh
pip install torbot==4.2.0
```

### Added
- Added optional `torbot app` and `torbot --app` CLI commands for launching the TorBotApp desktop UI when installed separately.
- Added TorBotApp discovery through `--app-dir`, `TORBOT_APP_DIR`, or a sibling `TorBotApp` checkout.
- Documented the optional desktop app architecture while keeping Node and Electron out of the Python package dependencies.
- Added a safer publish helper that checks current-version artifacts and prompts for a PyPI token only when uploading.

### Fixed
- Moved the CLI implementation into the installed package so the `torbot` console command works from a wheel install.
- Removed the deprecated `sklearn` package dependency in favor of the existing `scikit-learn` dependency so fresh installs do not fail.

## 4.1.1

### Fixed
Expand Down
41 changes: 41 additions & 0 deletions docs/DESKTOP_APP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Desktop App Architecture

TorBot is CLI-first. The optional desktop experience is provided by
[TorBotApp](https://github.com/KingAkeem/TorBotApp), a separate Electron and
React project.

Keeping the app separate preserves a lightweight Python install for CLI users:

- TorBot owns the Python package, command-line crawler, and library modules.
- TorBotApp owns the desktop UI, Electron packaging, and Node dependency tree.
- Backend crawl services expose the API consumed by TorBotApp.

## Launching from TorBot

The CLI can launch TorBotApp when it is installed locally:

```sh
torbot app
```

Discovery order:

1. `--app-dir /path/to/TorBotApp`
2. `TORBOT_APP_DIR=/path/to/TorBotApp`
3. A sibling `TorBotApp` directory next to this repository

If the app is not found, TorBot prints setup guidance and exits without changing
normal CLI behavior.

## Expected development layout

```text
code/
+-- TorBot/
+-- TorBotApp/
+-- gotor/
```

TorBotApp currently talks to the GoTor job-control API. If this Python package
later grows a compatible `torbot serve` API, TorBotApp can support either
backend without making Electron a required dependency of the CLI.
186 changes: 3 additions & 183 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,192 +1,12 @@
#!/usr/bin/env python3

import os
import argparse
import sys
import logging
import toml
import httpx
from torbot.cli import get_version, main, run, set_arguments

from torbot.modules.api import get_ip
from torbot.modules.color import color
from torbot.modules.updater import check_version
from torbot.modules.info import execute_all, fetch_html
from torbot.modules.linktree import LinkTree


def print_tor_ip_address(client: httpx.Client) -> None:
"""
https://check.torproject.org/ tells you if you are using tor and it
displays your IP address which we scape and display
"""
resp = get_ip(client)
print(resp["header"])
print(color(resp["body"], "yellow"))


def print_header(version: str) -> None:
"""
Prints the TorBot banner including version and license.
"""
license_msg = color("LICENSE: GNU Public License v3", "red")
banner = r"""
__ ____ ____ __ ______
/ /_/ __ \/ __ \/ /_ ____/_ __/
/ __/ / / / /_/ / __ \/ __ \/ /
/ /_/ /_/ / _, _/ /_/ / /_/ / /
\__/\____/_/ |_/_____/\____/_/ v{VERSION}
""".format(VERSION=version)
banner = color(banner, "red")

title = r"""
{banner}
#######################################################
# TorBot - Dark Web OSINT Tool #
# GitHub : https://github.com/DedsecInside/TorBot #
# Help : use -h for help text #
#######################################################
{license_msg}
"""

title = title.format(license_msg=license_msg, banner=banner)
print(title)


def run(arg_parser: argparse.ArgumentParser, version: str) -> None:
args = arg_parser.parse_args()

# setup logging
date_fmt = "%d-%b-%y %H:%M:%S"
logging_fmt = "%(asctime)s - %(levelname)s - %(message)s"
logging_lvl = logging.DEBUG if args.v else logging.INFO
logging.basicConfig(level=logging_lvl, format=logging_fmt, datefmt=date_fmt)

# Print verison then exit
if args.version:
print(f"TorBot Version: {version}")
sys.exit()

# check version and update if necessary
if args.update:
check_version()
sys.exit()

# URL is required for crawl-related actions
if not args.url:
arg_parser.print_help()
sys.exit()

socks5_host = args.host
socks5_port = str(args.port)
socks5_proxy = f"socks5://{socks5_host}:{socks5_port}"
with httpx.Client(
timeout=60, proxies=socks5_proxy if not args.disable_socks5 else None
) as client:
# print header and IP address if not set to quiet
if not args.quiet:
print_header(version)
print_tor_ip_address(client)

if args.info:
execute_all(client, args.url)

tree = LinkTree(url=args.url, depth=args.depth, client=client)
tree.load()

# save data if desired
if args.save == "tree":
tree.save()
elif args.save == "json":
tree.saveJSON()

if args.html == "display":
fetch_html(client, args.url, tree)
elif args.html == "save":
fetch_html(client, args.url, tree, save_html=True)

# always print something, table is the default
if args.visualize == "table" or not args.visualize:
tree.showTable()
elif args.visualize == "tree":
print(tree)
elif args.visualize == "json":
tree.showJSON()

print("\n\n")


def set_arguments() -> argparse.ArgumentParser:
"""
Parses user flags passed to TorBot
"""
parser = argparse.ArgumentParser(
prog="TorBot", usage="Gather and analayze data from Tor sites."
)
parser.add_argument(
"-u",
"--url",
type=str,
default=None,
help="Specifiy a website link to crawl",
)
parser.add_argument(
"--depth", type=int, help="Specifiy max depth of crawler (default 1)", default=1
)
parser.add_argument(
"--host", type=str, help="IP address for SOCKS5 proxy", default="127.0.0.1"
)
parser.add_argument("--port", type=int, help="Port for SOCKS5 proxy", default=9050)
parser.add_argument(
"--save", type=str, choices=["tree", "json"], help="Save results in a file"
)
parser.add_argument(
"--visualize",
type=str,
choices=["table", "tree", "json"],
help="Visualizes data collection.",
)
parser.add_argument("-q", "--quiet", action="store_true")
parser.add_argument(
"--version", action="store_true", help="Show current version of TorBot."
)
parser.add_argument(
"--update",
action="store_true",
help="Update TorBot to the latest stable version",
)
parser.add_argument(
"--info",
action="store_true",
help="Info displays basic info of the scanned site. Only supports a single URL at a time.",
)
parser.add_argument("-v", action="store_true", help="verbose logging")
parser.add_argument(
"--disable-socks5",
action="store_true",
help="Executes HTTP requests without using SOCKS5 proxy",
)
parser.add_argument(
"--html",
choices=["save", "display"],
help="Saves / Displays the html of the onion link",
)

return parser
__all__ = ["get_version", "main", "run", "set_arguments"]


if __name__ == "__main__":
try:
arg_parser = set_arguments()
config_file_path = os.path.join(
os.path.dirname(os.path.realpath(__file__)), "pyproject.toml"
)
try:
with open(config_file_path, "r") as f:
data = toml.load(f)
version = data["project"]["version"]
except Exception as e:
raise Exception("unable to find version from pyproject.toml.\n", e)

run(arg_parser, version)
main()
except KeyboardInterrupt:
print("Interrupt received! Exiting cleanly...")
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "torbot"
version = "4.1.1"
version = "4.3.0"
authors = [
{ name="Akeem King", email="akeemtlking@gmail.com" },
{ name="PS Narayanan", email="thepsnarayanan@gmail.com" },
Expand Down Expand Up @@ -38,7 +38,6 @@ dependencies = [
"scikit-learn>=1.5.0",
"scipy>=1.13.0",
"six>=1.16.0",
"sklearn>=0.0",
"soupsieve>=2.8.4",
"termcolor>=2.4.0",
"texttable>=1.7.0",
Expand Down
7 changes: 3 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ certifi==2024.7.4
charset-normalizer==3.3.2
colorama==0.4.6 ; sys_platform == "win32"
decorator==5.1.1
h11==0.14.0
httpcore==1.0.5
h11==0.16.0
httpcore==1.0.9
httpx[socks]==0.27.0
idna==3.15
igraph==0.11.4
Expand All @@ -27,9 +27,8 @@ python-dotenv==1.2.2
pywin32-ctypes==0.2.2 ; sys_platform == "win32"
scikit-learn==1.5.0
scipy==1.13.0
setuptools==78.1.1
setuptools==83.0.0
six==1.16.0
sklearn==0.0
sniffio==1.3.1
socksio==1.0.0
soupsieve==2.8.4
Expand Down
Loading