# 🧅 Onionfy

Route any Python code through Tor - simple, universal, powerful.

**The ProxyChains alternative for Windows** - Easy to use, zero configuration, works with any Python library.

## Why Onionfy?

Onionfy makes it effortless to route your Python applications through the Tor network for enhanced privacy and anonymity. Unlike traditional solutions like ProxyChains (which doesn't work natively on Windows), Onionfy provides a pure Python solution that works seamlessly across all platforms.

### Key Benefits

- **Windows Native**: No need for WSL or Linux compatibility layers
- **Zero Configuration**: Works out of the box with sensible defaults
- **Universal Compatibility**: Works with requests, urllib, Selenium, aiohttp, and any library that supports proxies
- **Automatic Tor Management**: Automatically starts and manages Tor process
- **Developer Friendly**: Simple API, context managers, and comprehensive error handling
- **Production Ready**: Built-in connection verification, configurable timeouts, and retry logic

### Use Cases

- **Web Scraping**: Avoid IP bans and rate limiting by rotating through Tor exit nodes
- **Privacy-Focused Applications**: Build applications that respect user privacy
- **Security Research**: Conduct security testing and research anonymously
- **API Testing**: Test geo-restricted APIs from different locations
- **Data Collection**: Gather data without revealing your identity or location
- **Bypass Restrictions**: Access content that may be blocked in your region
- **Choose Exit Country**: Make it appear as if you're browsing from a specific country (US, UK, Germany, etc.)

## Installation

### 1. Install Tor Browser

**Onionfy requires Tor to be installed on your system.**

Download and install Tor Browser from: https://www.torproject.org/download/

- **Windows**: Install to default location `C:\Program Files\Tor Browser\`
- **Linux/Mac**: Install via package manager or download from official site

### 2. Install Onionfy

```bash
pip install onionfy
```

## Quick Start

```python
from onionfy import OnionClient
import json

# Example 1: Get IP information
try:
    client = OnionClient()
    response = client.session.get("http://ip-api.com/json/")
    
    if response.status_code == 200:
        print("--- IP Information ---")
        print(json.dumps(response.json(), indent=4, ensure_ascii=False))
except Exception as e:
    print(f"An error occurred: {e}")

# Example 2: Get random user data
try:
    client = OnionClient()
    response = client.session.get("https://randomuser.me/api/")
    
    if response.status_code == 200:
        print("--- Random User Data ---")
        print(json.dumps(response.json(), indent=4, ensure_ascii=False))
except Exception as e:
    print(f"An error occurred: {e}")
```

### Simple Usage

```python
from onionfy import OnionClient

# Just works - no config needed
client = OnionClient()
response = client.get("https://httpbin.org/ip")
print(response.json())

# Override settings
client = OnionClient(socks_port=9150, timeout=60)

# Custom config file
client = OnionClient(config_path="custom.conf")

# Context manager (auto cleanup)
with OnionClient() as client:
    response = client.get("https://api.example.com/data")
    print(response.text)
```

## Configuration

Configuration is **optional**. The library includes a default config file.

### Option 1: No config file (use library defaults)
```python
client = OnionClient()  # Uses built-in onionfy.conf from library
```

### Option 2: Global config (override defaults for all projects)
Create `C:\Users\YourName\.onionfy.conf`:
```ini
[tor]
tor_path = C:\Program Files\Tor Browser\Browser\TorBrowser\Tor\tor.exe
socks_host = 127.0.0.1
socks_port = 9050
control_host = 127.0.0.1
control_port = 9051
control_password = 
auto_start = true
timeout = 30

[network]
max_retries = 1
request_timeout = 30
verify_ssl = true
exit_nodes = 
exclude_exit_nodes = 
strict_nodes = false

[privacy]
user_agent = Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
disable_cookies = false
disable_cache = false

[logging]
log_level = INFO
log_file = 
log_to_console = false
```

### Option 3: Project-specific config
Create `onionfy.conf` in your project directory (overrides global config).

### Option 4: Custom config path
```python
client = OnionClient(config_path="C:\\path\\to\\custom.conf")
```

### Config Priority (highest to lowest):
1. Parameters passed to OnionClient()
2. Custom path (if specified)
3. Current directory `onionfy.conf`
4. User home `~/.onionfy.conf`
5. Library's built-in `onionfy.conf`

## Configuration File Explained

Each setting in the config file controls specific behavior:

### [tor] Section

**tor_path**
- Path to the Tor executable (tor.exe)
- Default: `C:\Program Files\Tor Browser\Browser\TorBrowser\Tor\tor.exe`
- Change this if Tor is installed in a different location

**socks_host**
- SOCKS proxy host address
- Default: `127.0.0.1` (localhost)
- Usually no need to change unless using remote Tor

**socks_port**
- SOCKS proxy port number
- Default: `9050`
- Change if another service uses this port or using Tor Browser (use `9150`)

**control_host**
- Tor control port host address
- Default: `127.0.0.1`
- Used for IP rotation and advanced Tor control

**control_port**
- Tor control port number
- Default: `9051`
- Used for sending commands to Tor

**control_password**
- Password for Tor control port authentication
- Default: empty (no password)
- Set if your Tor instance requires authentication

**auto_start**
- Automatically start Tor process when creating OnionClient
- Default: `true`
- Set to `false` if Tor is already running externally

**timeout**
- Maximum seconds to wait for Tor to start
- Default: `30`
- Increase if Tor takes longer to start on your system

### [network] Section

**max_retries**
- Number of retry attempts for failed requests
- Default: `1`
- Increase for unreliable connections

**request_timeout**
- Timeout in seconds for HTTP requests
- Default: `30`
- Applied automatically to all `get()` and `post()` calls

**verify_ssl**
- Verify SSL certificates for HTTPS requests
- Default: `true`
- Set to `false` only for testing (not recommended for production)

**exit_nodes**
- Comma-separated country codes for preferred exit nodes
- Default: empty (random exit nodes)
- Example: `US,GB,DE` for US, UK, Germany

**exclude_exit_nodes**
- Comma-separated country codes to avoid as exit nodes
- Default: empty (no exclusions)
- Example: `CN,RU` to avoid China and Russia

**strict_nodes**
- Enforce exit node preferences strictly
- Default: `false`
- Set to `true` to only use specified exit nodes

### [privacy] Section

**user_agent**
- HTTP User-Agent header sent with requests
- Default: `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36`
- Automatically applied to all requests through the session
- Change to mimic different browsers or devices

**disable_cookies**
- Disable cookie handling in requests
- Default: `false`
- Set to `true` for enhanced privacy

**disable_cache**
- Disable HTTP caching
- Default: `false`
- Set to `true` to prevent cache-based tracking

### [logging] Section

**log_level**
- Logging verbosity level
- Default: `INFO`
- Options: `DEBUG`, `INFO`, `WARNING`, `ERROR`

**log_file**
- Path to log file
- Default: empty (no file logging)
- Example: `C:\logs\onionfy.log`

**log_to_console**
- Enable console logging output
- Default: `false`
- Set to `true` to see logs in terminal

## Universal Proxy Usage

Use Tor with **any** Python library:

### With requests
```python
import requests
from onionfy import OnionClient

client = OnionClient()
response = requests.get("https://httpbin.org/ip", proxies=client.proxies)
print(response.json())
```

### With urllib
```python
import urllib.request
from onionfy import OnionClient

client = OnionClient()
client.set_system_proxy()  # Set environment variables
response = urllib.request.urlopen("https://httpbin.org/ip")
print(response.read())
```

### With Selenium
```python
from selenium import webdriver
from onionfy import OnionClient

client = OnionClient()
options = webdriver.ChromeOptions()
options.add_argument(f'--proxy-server={client.proxy_url}')
driver = webdriver.Chrome(options=options)
```

### With aiohttp
```python
import aiohttp
from onionfy import OnionClient

client = OnionClient()
async with aiohttp.ClientSession() as session:
    async with session.get("https://httpbin.org/ip", proxy=client.proxy_url) as resp:
        print(await resp.json())
```

## Features

- 🚀 **Zero config** - Works out of the box
- 🔌 **Universal proxy** - Use with any Python library via `proxy_url` or `proxies`
- ⚡ **Auto-start Tor** - Automatically launches and manages Tor process
- ✅ **Connection verification** - Check if you're actually using Tor
- 🧹 **Context manager** - Clean resource management
- 🛡️ **Error handling** - Clear error messages
- 📦 **Built-in session** - Pre-configured requests session

## API Reference

### OnionClient

```python
client = OnionClient(
    config_path="onionfy.conf",  # Optional: custom config path
    tor_path=r"C:\Program Files\Tor Browser\Browser\TorBrowser\Tor\tor.exe",
    socks_host="127.0.0.1",
    socks_port=9050,
    auto_start=True,
    timeout=30,
    verbose=False  # Enable logging output
)
```

All parameters are optional and override config file settings.

### Properties

- `client.proxy_url` - Returns `"socks5h://127.0.0.1:9050"` for any library
- `client.proxies` - Returns `{"http": "...", "https": "..."}` dict for requests
- `client.session` - Pre-configured requests.Session object

### Methods

- `client.start()` - Start Tor process
- `client.stop()` - Stop Tor process
- `client.check_connection()` - Verify Tor connection, returns `{"ip": "...", "is_tor": True}`
- `client.set_system_proxy()` - Set environment variables for system-wide proxy
- `client.get(url, **kwargs)` - HTTP GET through Tor
- `client.post(url, **kwargs)` - HTTP POST through Tor

## Examples

### Check your Tor IP
```python
client = OnionClient()
info = client.check_connection()
print(f"IP: {info['ip']}, Using Tor: {info['is_tor']}")
```

### Web scraping
```python
from bs4 import BeautifulSoup

client = OnionClient()
response = client.get("https://example.com")
soup = BeautifulSoup(response.text, 'html.parser')
print(soup.title.text)
```

### API requests through Tor
```python
client = OnionClient()
response = client.get("https://api.example.com/data")
data = response.json()
```

### Custom tor.exe location
```python
client = OnionClient(tor_path=r"C:\custom\path\tor.exe")
```

### Choose Your Exit Country (Beginner-Friendly)

**What is an exit node?**
When you use Tor, your traffic exits through a "exit node" before reaching the website. The website sees the exit node's IP address and location, not yours.

**Why choose exit country?**
- Access content only available in specific countries (e.g., if a website only accepts US traffic, set exit to US)
- Test how your website looks from different locations
- Bypass geo-restrictions and access region-locked content
- Appear as if you're browsing from a different country

**Examples:**

```python
# Appear as if you're browsing from the United States
client = OnionClient(exit_nodes="US")

# Appear from United Kingdom
client = OnionClient(exit_nodes="GB")

# Choose from multiple countries (US, UK, or Germany)
client = OnionClient(exit_nodes="US,GB,DE")

# Avoid specific countries (won't use China or Russia)
client = OnionClient(exclude_exit_nodes="CN,RU")

# ONLY use specified countries (strict mode)
client = OnionClient(exit_nodes="US,GB", strict_nodes=True)
```

**Common country codes:**
- `US` - United States
- `GB` - United Kingdom  
- `DE` - Germany
- `FR` - France
- `CA` - Canada
- `AU` - Australia
- `JP` - Japan
- `NL` - Netherlands

**Note:** Using specific exit nodes may be slower as there are fewer available servers.

### Enable verbose logging
```python
# See detailed Tor startup and connection logs
client = OnionClient(verbose=True)
response = client.get("http://ip-api.com/json/")
data = response.json()
print(f"IP: {data['query']}, Country: {data['country']}")
```

## Requirements

- **Tor Browser or standalone Tor** (Required - Download from https://www.torproject.org/download/)
- Python 3.8+
- requests[socks]

## How It Works

Onionfy acts as a bridge between your Python code and the Tor network:

1. **Automatic Tor Launch**: When you create an OnionClient, it automatically starts a Tor process in the background
2. **SOCKS5 Proxy**: Tor creates a SOCKS5 proxy on localhost (default port 9050)
3. **Request Routing**: All your HTTP/HTTPS requests are routed through this proxy
4. **Tor Network**: Your traffic goes through the Tor network, exiting from a random exit node
5. **Clean Shutdown**: When done, Onionfy cleanly stops the Tor process

This means your real IP address is hidden, and the target server only sees the Tor exit node's IP.

## Comparison with Alternatives

| Feature | Onionfy | ProxyChains | Manual Tor Setup |
|---------|---------|-------------|------------------|
| Windows Support | ✅ Native | ❌ Requires WSL | ⚠️ Manual |
| Zero Config | ✅ Yes | ❌ Needs config | ❌ Complex |
| Auto Tor Start | ✅ Yes | ❌ Manual | ❌ Manual |
| Python Native | ✅ Yes | ❌ System-level | ⚠️ Partial |
| Context Manager | ✅ Yes | ❌ No | ❌ No |
| Connection Verify | ✅ Built-in | ❌ Manual | ❌ Manual |
| Library Agnostic | ✅ Yes | ✅ Yes | ⚠️ Depends |

## Troubleshooting

### Tor fails to start
- Ensure Tor Browser is installed in the default location
- Check if another process is using port 9050
- Try specifying a custom tor_path: `OnionClient(tor_path=r"C:\path\to\tor.exe")`

### Connection timeout
- Increase timeout: `OnionClient(timeout=60)`
- Check your internet connection
- Verify Tor isn't blocked by your firewall

### Requests fail through Tor
- Verify Tor connection: `client.check_connection()`
- Enable verbose logging: `OnionClient(verbose=True)`
- Some websites block Tor exit nodes - this is expected behavior

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## Security Notice

While Onionfy routes your traffic through Tor, remember:
- Tor provides anonymity, not encryption for your data
- Always use HTTPS for sensitive data
- Some websites actively block Tor exit nodes
- Tor is slower than direct connections
- Don't use Tor for illegal activities

## License

MIT
