Skip to content
aneek0Public

Repository files navigation

VLESS Toolkit (vtk)

Parse, validate, and convert proxy links & subscriptions between formats. Three interfaces — Telegram bot, web (FastAPI), CLI — sharing one core.

Supported protocols

  • vless://
  • vmess://
  • trojan://
  • ss:// (SIP002 + legacy)
  • ssr://
  • hysteria2://
  • socks://
  • happ://crypt* (auto-decrypted)
  • incy://crypt* (auto-decrypted)

Supported output formats

Format Description
singbox sing-box JSON config (outbounds array)
mihomo mihomo/clash-meta YAML proxy list
flclash Full FlClash YAML config (mixed-port, proxy-groups & rules)
txt Plain text — one share link per line

Quick start

git clone <repo-url> && cd vtk
uv sync --all-extras        # core + bot + web
uv sync --extra web         # or: just the web interface
uv sync --extra bot         # or: just the Telegram bot

Core (installed always): httpx, pycryptodome, cryptography, pyyaml. Optional: [bot] — aiogram; [web] — fastapi, uvicorn, python-multipart, jinja2. orjson is used if present (optional speedup, not required).

Environment variables

Variable Description
VTK_BOT_TOKEN Telegram bot token (for bot interface)
VTK_PROXY_BASE Base host for passthrough /p/<params>/<url> links (default https://vtk.aneeko.qzz.io)

CLI

uv run vtk check vless://...                            # validate link
uv run vtk parse-cmd vless://...                         # parse & display fields
uv run vtk convert-cmd -l vless://... -f singbox         # convert single link
uv run vtk sub https://example.com/sub -f flclash        # fetch subscription
uv run vtk batch links.txt -f mihomo                     # batch convert file
uv run vtk extract config.json                           # config → share links
uv run vtk settings show                                 # show settings
uv run vtk settings set sub_format flclash               # change setting

Bot

export VTK_BOT_TOKEN=xxx
uv run python -m bot.main

Commands: /start, /help, /settings, /proxy

Features:

  • Auto-detects input type (link / subscription URL / config / TXT)
  • Inline keyboard for per-type output format settings
  • 📱 Proxy device menu: toggles for "send device headers" + HWID, OS switch, Randomize, Clear
  • Import device settings from a pasted web app-link (https://host/p/android,ver=…,ua=…,hwid=…/https://sub…)
  • File upload support (documents processed same as text)
  • Rate limiting (3 msg/sec per user)
  • happ://crypt* auto-decryption
  • incy://crypt* auto-decryption

Web

uv run python -m web.main
# http://localhost:9000

HTML form + JSON API endpoints:

  • GET /api/convert?input=...&format=singbox — convert
  • GET /api/extract?input=... — config → share links
  • GET /api/check?link=... — validate single link
  • GET /api/device/random — random device fingerprint params (shared with bot)
  • Convert tab has optional device-header fields (UA/HWID/OS/Ver/Model/Locale) + "Send device headers" toggle + Randomize
  • PROXY tab builds /p/<params>/<url> app-links (host = VTK_PROXY_BASE)

Project structure

core/
  __init__.py    — exports
  logic.py       — parse links, fix_link(), fetch subscriptions, extract_country()
  converters.py  — singbox / mihomo / flclash / txt output
  reverse.py     — config → share links (sing-box / mihomo YAML)
  settings.py    — per-input-type format defaults
  happ.py        — happ://crypt* offline decryptor (all RSA keys bundled)
  incy.py        — incy://crypt* offline decryptor (AES-256-GCM, keymat bundled)
bot/             — Telegram bot (aiogram 3)
web/             — FastAPI + HTML form + JSON API
cli/             — CLI (typer)

Core API highlights

fix_link()

Normalizes proxy links for cross-client compatibility (e.g. podkop):

  • Converts & → ? at query start
  • Adds type=raw if missing (vless)
  • Normalizes packet-encoding= → packetEncoding=

Node validation

Node.validate() checks required fields for each protocol (lightweight, no external deps):

node = parse_link(link)
node.validate()  # raises ParseError on critical issues

Called automatically in parse_text_input() and iter_parse_text().

Node round-trip

Each protocol has a to_*_link() method for serialization:

node = parse_vless(link)
link2 = node.to_vless_link()  # round-trip

Streaming parser

For large inputs, use iter_parse_text() (generator) instead of parse_text_input().

Country extraction

extract_country(name) detects country from flag emoji or text patterns in node names. Used by FlClash group_by_country to create per-country proxy groups.

Protocol adapters (converters)

converters.py uses a ProtocolAdapter pattern (ABC) for format-specific generation:

  • VLESSAdapter, VMessAdapter, TrojanAdapter, SSAdapter, Hysteria2Adapter, SSRAAdapter
  • Each adapter implements to_clash_dict(), to_mihomo_dict(), to_singbox_dict(), to_link()
  • YAML output via PyYAML (no manual indentation)

Settings

Stored in ~/.config/vtk/settings.json. Each input type has its own default output format: sub_format, link_format, config_format, txt_format.

{
  "sub_format": "mihomo",
  "link_format": "singbox",
  "config_format": "txt",
  "txt_format": "mihomo",
  "tag_prefix": "",
  "timeout": 15,
  "group_by_country": false
}

Tests

pytest tests/

Covers: fix_link normalization, all protocol parsers, all converters, round-trip, country extraction, error handling.

Development

uv sync --all-extras            # runtime + dev group
uv run pytest -q                # tests
uv run ruff check .             # lint
uvx pre-commit run --all-files  # git hooks (ruff)

CI (GitHub Actions): ruff + pytest on Python 3.10 & 3.13 + pip-audit. Dependabot watches pip and github-actions weekly.

Bundled data (data/)

  • crypt5_keys.json — RSA private keys for the happ://crypt5 offline decryptor
  • incy_keymat.json — AES-256-GCM key material for the incy://crypt1 decryptor
  • incy_vectors.json — test vectors generated by the official @incy/link-encoder package (see scripts/gen-vectors.mjs)

These files ship inside the wheel (setuptools package-data) — the decryptors work fully offline.

Releases

Packages

Contributors

Languages