1. KBKO VPN Bot â Telegram Auto-Sale Bot
What it is: A Telegram bot that sells VPN accounts automatically. Customer pays via WavePay, sends a payment screenshot, admin taps approve, and the bot creates the VPN account instantly through the Marzban API. Serves two Marzban panels (Spain + USA).
config.py â the sales rules
One file controls what the bot is allowed to sell. Changing the product line is a one-line edit, not a code hunt:
# /root/vpnbot/config.py
SOLD_PROTOCOLS = ["vless", "shadowsocks"] # VMess/Trojan retired â one-line product change
PLANS = {
"gb30": {"gb": 30, "days": 30, "price": 3000, "label": "30GB ¡ 30 days"},
"gb100": {"gb": 100, "days": 30, "price": 5000, "label": "100GB ¡ 30 days"},
}
TRIAL_GB = 1
TRIAL_DAYS = 1
TRIAL_SERVER = "es" # trials are Spain-only, USA is premium-only
REFERRAL_GB = 1 # +1GB per referred friend
ADMIN_IDS = [8500073574]
marzban.py â API client with token caching
Every API call needs a Bearer token. Logging in on every call would spam the panel's Telegram #Login alerts, so the client logs in once and reuses the token until the panel returns 401:
# /root/vpnbot/marzban.py
import requests, time
class Marzban:
def __init__(self, base_url, username, password):
self.base_url = base_url.rstrip("/")
self.username = username
self.password = password
self._token = None
def _login(self):
r = requests.post(
f"{self.base_url}/api/admin/token",
data={"username": self.username, "password": self.password},
verify=False, timeout=15)
r.raise_for_status()
self._token = r.json()["access_token"]
def _req(self, method, path, retry=True, **kw):
if not self._token:
self._login()
headers = kw.pop("headers", {})
headers["Authorization"] = f"Bearer {self._token}"
r = requests.request(method, f"{self.base_url}{path}",
headers=headers, verify=False,
timeout=20, **kw)
if r.status_code == 401 and retry:
self._login() # token expired -> one fresh login
return self._req(method, path, retry=False, **kw)
r.raise_for_status()
return r.json() if r.text else None
def create_user(self, username, gb, days):
# Enforce the sales rule: ONLY VLESS Reality + Shadowsocks.
# Even if the panel has VMess/Trojan inbounds, the user never gets them.
proxies = {p: {} for p in SOLD_PROTOCOLS}
inbounds = {"vless": ["VLESS TCP REALITY"],
"shadowsocks": ["Shadowsocks TCP"]}
return self._req("POST", "/api/user", json={
"username": username,
"proxies": proxies,
"inbounds": inbounds,
"data_limit": gb * 1024**3,
"expire": int(time.time()) + days * 86400,
# Defaults for every new user (both panels verified compatible):
"flow": "xtls-rprx-vision", # VLESS Reality flow
})
# NOTE: Shadowsocks method aes-256-gcm is set on the panel inbound itself.
bot.py â buy flow with server picker
Every command has both inline buttons and a working /cmd route. The buy flow starts with a server choice:
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.ext import ContextTypes
async def buy(update: Update, context: ContextTypes.DEFAULT_TYPE):
kb = [
[InlineKeyboardButton("đşđ¸ USA ¡ Premium â", callback_data="srv_us")],
[InlineKeyboardButton("đŞđ¸ Spain", callback_data="srv_es")],
]
await update.message.reply_text(
"Server áá˝áąá¸á፠/ Choose server:",
reply_markup=InlineKeyboardMarkup(kb))
async def on_server(update: Update, context: ContextTypes.DEFAULT_TYPE):
q = update.callback_query
await q.answer()
server = q.data.replace("srv_", "") # "us" or "es"
context.user_data["server"] = server
kb = [[InlineKeyboardButton(p["label"], callback_data=f"plan_{code}")]
for code, p in PLANS.items()]
await q.edit_message_text("Plan áá˝áąá¸ááŤ:",
reply_markup=InlineKeyboardMarkup(kb))
bot.py â WavePay screenshot â one-tap approve
Semi-auto payments: the customer sends a screenshot, the admin gets it with an Approve button, and approval creates the account immediately:
async def on_payment_photo(update: Update, context: ContextTypes.DEFAULT_TYPE):
uid = update.effective_user.id
plan = context.user_data.get("plan")
photo = update.message.photo[-1].file_id
kb = [[InlineKeyboardButton("â
Approve",
callback_data=f"appr_{uid}_{plan}")]]
await context.bot.send_photo(
ADMIN_IDS[0], photo,
caption=f"đ° Payment from {uid} ¡ plan {plan}",
reply_markup=InlineKeyboardMarkup(kb))
await update.message.reply_text("âł Admin á
á
áşááąáááş, ááá
áąáŹááˇáşâŚ")
async def on_approve(update: Update, context: ContextTypes.DEFAULT_TYPE):
q = update.callback_query
await q.answer()
_, uid, plan = q.data.split("_", 2)
server = user_server(uid) # stored at buy time
mz = Marzban(**PANELS[server]) # Spain or USA panel
p = PLANS[plan]
try:
u = mz.create_user(f"u{uid}", p["gb"], p["days"])
save_order(uid, plan, server) # SQLite: users/orders tables
await context.bot.send_message(
int(uid),
f"â
áááźáŽ!\n\n{p['label']}\n\n"
f"Subscription:\n<code>{u['subscription_url']}</code>\n\n"
f"Happ / Hiddify áá˛áááˇáş, connect áážáááş â áááźáŽ đ",
parse_mode="HTML")
await q.edit_message_caption(caption="â
Approved & created")
except Exception as e:
await q.edit_message_caption(caption=f"â Failed: {e}")
Deployment
# Systemd â Restart=always so a crash never needs a human
sudo tee /etc/systemd/system/vpnbot.service << 'EOF'
[Unit]
Description=KBKO VPN Bot
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/root/vpnbot
ExecStart=/root/vpnbot/venv/bin/python bot.py
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload && sudo systemctl enable --now vpnbot
Hard lessons: Marzban sends a Telegram #Login alert on every successful /api/admin/token â set the bot admin's telegram_id to NULL in the panel DB or your phone drowns in spam. Keep telegram_id on your human admin so real logins still alert. For monitoring, use non-authenticated reachability checks only.
2. Marzban VPN Infrastructure (Spain + USA)
What it is: Two production Marzban panels (Spain 31.70.155.75, USA 198.251.70.226) serving VLESS Reality and Shadowsocks via Xray-core. Hardened with iptables, fronted by nginx for public subscriptions.
Why VLESS is on port 443
Tested with Atom mobile data: Shadowsocks :1080 connected, but VLESS :2053, VMess :2080 and Trojan :2081 all timed out. The carrier filters uncommon ports. Port 443 (HTTPS) is never filtered, so VLESS Reality moved there:
# On the USA panel host â backup first, always
cp /var/lib/marzban/xray_config.json /var/lib/marzban/xray_config.json.bak2053
# In the Marzban panel: inbounds -> VLESS TCP REALITY -> port 2053 -> 443
# Then restart the container:
cd /opt/marzban && docker compose restart marzban
# Verify xray is actually listening on 443
ss -tlnp | grep ':443' # expect: xray
# Existing customers must refresh their subscription to get the new port.
Firewall â panel locked to one IP
The Marzban dashboard (:8000) is only reachable from the Spain server. Everything else is dropped at the kernel level, before Marzban ever sees it:
# Run ON the USA Marzban host
iptables -A INPUT -i lo -j ACCEPT # localhost always
iptables -A INPUT -p tcp --dport 8000 -s 31.70.155.75 -j ACCEPT # Spain only
iptables -A INPUT -p tcp --dport 8000 -j DROP # everyone else
iptables-save > /etc/iptables/rules.v4 # persist reboot
nginx â public subscriptions without VPN
Problem: customers buy VPN but needed VPN just to fetch their subscription link (both live on :8000). Fix: nginx on :8443 proxies only /sub/* to Marzban; /, /dashboard, /api all return 404. Note Marzban's :8000 is HTTPS with a self-signed cert, so the proxy disables upstream verification:
# /etc/nginx/sites-available/mm-sub-proxy
server {{
listen 8443 ssl;
server_name mm.kxant.eu.cc;
ssl_certificate /etc/letsencrypt/live/mm.kxant.eu.cc/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mm.kxant.eu.cc/privkey.pem;
location /sub/ {{
proxy_pass https://127.0.0.1:8000; # Marzban is HTTPS (self-signed)
proxy_ssl_verify off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}}
location / {{ return 404; }} # dashboard stays Spain-only
}}
# Point new subscription URLs at the open port.
# /opt/marzban/.env:
XRAY_SUBSCRIPTION_URL_PREFIX="https://mm.kxant.eu.cc:8443"
# IMPORTANT: `docker compose restart` does NOT re-read .env.
# Recreate the container:
cd /opt/marzban && docker compose up -d
docker exec marzban-marzban-1 env | grep XRAY_SUBSCRIPTION_URL_PREFIX
Silence the bot-login Telegram spam
Marzban notifies the linked Telegram ID on every admin login â and the bot logs in constantly. Remove the link from the bot admin only; keep it on the human admin:
python3 -c "
import sqlite3
c = sqlite3.connect('/var/lib/marzban/db.sqlite3')
c.execute(\"UPDATE admins SET telegram_id = NULL WHERE username = 'vpnbot'\")
c.commit()
print(list(c.execute('SELECT username, telegram_id FROM admins')))
"
# -> [('kbko', 8500073574), ('vpnbot', None)]
# Bot logins: silent. Your manual logins: still alert.
Hard lessons: Always cp xray_config.json{,.bak} before inbound edits. docker compose restart ignores .env changes â use up -d. Test ports from the user's real network (Atom), not from the server â server-side checks lie about carrier filtering.
3. PyHost Bot â Free Hosting Platform
What it is: A Telegram bot that gives users free Python hosting on the VPS. Users deploy scripts, the bot wraps each one in its own resource-capped systemd service. Premium tiers and one-time trial keys included.
Deployment model â one systemd service per user
Isolation is the whole design: a runaway script can only kill its own service, never the host or the VPN bot. Resource caps are non-negotiable:
import subprocess, textwrap
def deploy_script(user_id: int, filename: str):
svc = f"pyhost-{{user_id}}"
unit = textwrap.dedent(f"""
[Unit]
Description=PyHost user {{user_id}}
After=network.target
[Service]
Type=simple
User=nobody
WorkingDirectory=/root/pyhostbot/apps/{{user_id}}
ExecStart=/usr/bin/python3 {{filename}}
Restart=on-failure
# --- resource caps: one user can never take the server down ---
MemoryMax=512M
CPUQuota=100%
[Install]
WantedBy=multi-user.target
""")
open(f"/etc/systemd/system/{{svc}}.service", "w").write(unit)
subprocess.run(["systemctl", "daemon-reload"], check=True)
subprocess.run(["systemctl", "enable", "--now", svc], check=True)
Trial keys â PH-XXXXXXXX, one redeem per user
import secrets, sqlite3, time
def gen_key(days: int, max_uses: int, note: str = "") -> str:
key = "PH-" + secrets.token_hex(4).upper() # e.g. PH-9F3A2C1D
db.execute("INSERT INTO trial_keys(key,days,max_uses,used,note) VALUES(?,?,?,?,?)",
(key, days, max_uses, 0, note))
db.commit()
return key
def redeem_key(user_id: int, key: str) -> str:
row = db.execute("SELECT days,max_uses,used FROM trial_keys WHERE key=?",
(key,)).fetchone()
if not row: return "â Key ááážááşáá°á¸"
if row["used"] >= row["max_uses"]: return "â áᎠkey ááŻááşáá˝áŹá¸ááźáŽ"
if db.execute("SELECT 1 FROM redemptions WHERE key=? AND user_id=?",
(key, user_id)).fetchone():
return "â áááş áᎠkey ááŻáśá¸ááźáŽá¸ááźáŽ (áá
áşááąáŹááş áá
áşááŤáá˛)"
db.execute("UPDATE trial_keys SET used=used+1 WHERE key=?", (key,))
db.execute("INSERT INTO redemptions(key,user_id) VALUES(?,?)", (key, user_id))
# grant biggest-plan premium for `days`
db.execute("INSERT OR REPLACE INTO premiums(user_id,plan,expire) VALUES(?,?,?)",
(user_id, "pro", int(time.time()) + row["days"] * 86400))
db.commit()
return f"â
{{row['days']}} áááş Pro áááźáŽ!"
/admin panel â tappable commands
Admins get buttons, not a memorized command list. Every button maps to the same /cmd handler so both paths always work:
async def admin_panel(update, context):
kb = [
[InlineKeyboardButton("đ Plans", callback_data="adm_plans")],
[InlineKeyboardButton("đ Premiums", callback_data="adm_premiums")],
[InlineKeyboardButton("đ Keys", callback_data="adm_keys")],
[InlineKeyboardButton("â /addpremium", callback_data="adm_addpremium")],
]
await update.message.reply_text("Admin panel:",
reply_markup=InlineKeyboardMarkup(kb))
# Rule: every callback_data "adm_X" calls the same function as /X.
Hard lessons: Premium â admin â free deploy/renew stayed admin-only even for premium users. Cap memory per deployment or one infinite loop OOMs the whole VPS. Fake test doubles must mirror the real library API exactly, or tests pass and production crashes.
4. VidDrop â Telegram Video Downloader
What it is: Paste any video link â get quality buttons + MP3 option â the file arrives in chat. Powered by yt-dlp, capped at 2 concurrent downloads so the VPS never chokes.
Format listing â inline buttons
import yt_dlp, asyncio
def list_formats(url: str):
with yt_dlp.YoutubeDL({{"quiet": True}}) as ydl:
info = ydl.extract_info(url, download=False)
# pick one stream per height, skip absurd sizes
seen, out = set(), []
for f in info.get("formats", []):
h = f.get("height")
if h and h not in seen and h <= 1080:
seen.add(h)
out.append((h, f["format_id"]))
return info.get("title", "video"), sorted(out)
async def on_link(update, context):
title, fmts = await asyncio.to_thread(list_formats, update.message.text)
kb = [[InlineKeyboardButton(f"đŹ {{h}}p", callback_data=f"dl_{{fid}}_{{h}}")]
for h, fid in fmts]
kb.append([InlineKeyboardButton("đľ MP3", callback_data="dl_mp3")])
context.user_data["dl_url"] = update.message.text
await update.message.reply_text(f"{{title}}\nQuality áá˝áąá¸ááŤ:",
reply_markup=InlineKeyboardMarkup(kb))
Download with a semaphore (max 2 at once)
SEMAPHORE = asyncio.Semaphore(2)
async def on_quality(update, context):
q = update.callback_query
await q.answer()
url = context.user_data["dl_url"]
async with SEMAPHORE: # never more than 2 downloads
if q.data == "dl_mp3":
opts = {{"format": "bestaudio/best",
"postprocessors": [{{"key": "FFmpegExtractAudio",
"preferredcodec": "mp3"}}]}}
else:
_, fid, h = q.data.split("_")
opts = {{"format": fid, "max_filesize": 50 * 1024 * 1024}}
opts.update({{"outtmpl": "/tmp/viddrop/%(id)s.%(ext)s", "quiet": True}})
fname = await asyncio.to_thread(download, url, opts)
await q.message.reply_document(open(fname, "rb"))
os.remove(fname) # don't fill the disk
def download(url, opts):
with yt_dlp.YoutubeDL(opts) as ydl:
info = ydl.extract_info(url, download=True)
return ydl.prepare_filename(info)
Hard lessons: TikTok blocks datacenter IPs â the fix is the user's own logged-in cookies.txt passed via --cookies; never print cookie contents. 50MB is Telegram's bot file cap â enforce max_filesize before downloading, not after.
5. DubLab â AI Video Dubbing Website
What it is: Upload a short video (â¤5 min / 250MB) â pick ENâMM + male/female voice â get a dubbed MP4. Pipeline: faster-whisper transcribes â Gemini translates â Edge-TTS speaks (Burmese voices work!) â FFmpeg muxes. Plus an SRT translator tab.
Step 1 â transcribe with faster-whisper
from faster_whisper import WhisperModel
model = WhisperModel("small", device="cpu", compute_type="int8")
def transcribe(path: str):
segments, _ = model.transcribe(path, language="en")
return [{{"start": s.start, "end": s.end, "text": s.text.strip()}}
for s in segments if s.text.strip()]
# Dependency pins that actually work together (faster-whisper 1.2.1):
# av==15.1.0 + huggingface_hub<1
# av 19.x removed the `metadata_errors` kwarg faster-whisper passes -> TypeError.
# av 14.x has no cp312 wheel -> build fails. 15.1.0 is the sweet spot.
Step 2 â translate with Gemini key rotation
Free Gemini quota is tiny (~10â15 req/day/key â 1â2 videos/day). Rotate through keys; when one hits quota, try the next:
import google.generativeai as genai, itertools
KEYS = open("/root/dublab/.env").read().split("GEMINI_API_KEYS=")[1].split(",")
key_cycle = itertools.cycle(k.strip() for k in KEYS)
def translate(text: str, target: str) -> str:
last_err = None
for _ in range(len(KEYS)): # try each key once
genai.configure(api_key=next(key_cycle))
try:
m = genai.GenerativeModel("gemini-2.0-flash")
r = m.generate_content(
f"Translate to {{target}}, return ONLY the translation: {{text}}")
return r.text.strip()
except Exception as e:
last_err = e # quota error -> next key
raise last_err
Step 3 â dub with Edge-TTS (Burmese voices!)
import edge_tts, asyncio
VOICES = {{
"mm-male": "my-MM-ThihaNeural",
"mm-female": "my-MM-NilarNeural",
"en-male": "en-US-ChristopherNeural",
"en-female": "en-US-AriaNeural",
}}
async def speak(text: str, voice: str, out: str):
await edge_tts.Communicate(text, voice).save(out)
# Per segment, then stretch/compress each clip to the segment's
# timestamp window with FFmpeg `atempo` so lips roughly match.
Step 4 â mux with FFmpeg
# dubbed.mp3 = concatenated per-segment clips, original kept quiet underneath
subprocess.run([
"ffmpeg", "-y",
"-i", "original.mp4",
"-i", "dubbed.mp3",
"-filter_complex",
"[0:a]volume=0.25[orig];[1:a]adelay=0[db];[orig][db]amix=inputs=2[a]",
"-map", "0:v", "-map", "[a]",
"-c:v", "copy", "-c:a", "aac",
"output.mp4"], check=True)
Serving â waitress behind nginx, iOS-safe uploads
# waitress on 127.0.0.1 ONLY (never expose the app server directly)
# /etc/systemd/system/dublab.service:
# ExecStart=/root/dublab/venv/bin/waitress-serve --listen=127.0.0.1:5000 app:app
/* iOS Safari bug: fetch() POST with FormData throws
"TypeError: The string did not match the expected pattern" (WebKit bug).
Fix: XMLHttpRequest for uploads, fetch only for JSON polling. */
function uploadFile(file) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
const fd = new FormData();
fd.append("video", file);
xhr.open("POST", "/api/dub");
xhr.onload = () => resolve(JSON.parse(xhr.responseText));
xhr.onerror = reject;
xhr.send(fd);
});
}
Hard lessons: Jobs in process memory die on restart â an old job ID becomes invalid after any service restart. Rate-limit uploads (10/hour per IP) or one user eats the whole Gemini quota. The monitor must never auto-reopen the site against an explicit close order.
6. MyCinema â Movie Streaming Website
What it is: "Watch edition" streaming site on a Windows VPS â search any title, pick a server, watch instantly. 6,487 movies + 1,330 TV shows, all stream-resolvable. No download links.
Player â server URL templates
Three main servers plus 229 MoviesHUB per-file hub servers as extras. The app only swaps the iframe src:
const SERVERS = [
{{ name: "Server 1 ¡ VidSrc", movie: id => `https://vidsrc.to/embed/movie/${{id.imdb}}`,
tv: (id,s,e) => `https://vidsrc.to/embed/tv/${{id.imdb}}/${{s}}/${{e}}` }},
{{ name: "Server 2 ¡ MoviesAPI", movie: id => `https://moviesapi.to/movie/${{id.tmdb}}`,
tv: (id,s,e) => `https://moviesapi.to/tv/${{id.tmdb}}-${{s}}-${{e}}` }},
{{ name: "Server 3 ¡ VidLink", movie: id => `https://vidlink.pro/movie/${{id.tmdb}}`,
tv: (id,s,e) => `https://vidlink.pro/tv/${{id.tmdb}}/${{s}}/${{e}}` }},
];
function play(ids, serverIdx = 0, season = 1, ep = 1) {{
const s = SERVERS[serverIdx];
const url = ids.imdb && serverIdx === 0
? s.movie(ids) : s.tv(ids, season, ep);
document.getElementById("player").src = url;
saveResume(ids, serverIdx, season, ep); // 30-day TTL in localStorage
}}
Search via TMDB
const TMDB = "https://api.themoviedb.org/3";
async function search(q) {{
const r = await fetch(`${{TMDB}}/search/multi?api_key=${{API_KEY}}&query=${{encodeURIComponent(q)}}`);
const j = await r.json();
return j.results.filter(x => x.media_type === "movie" || x.media_type === "tv");
}}
// Catalog is pre-built (movies.json / tvshows.json) from TMDB â
// only titles resolvable by at least one server are included.
SRT subtitles â upload, convert, serve with CORS
VidSrc honors external subtitles if you hand it a VTT URL. The server converts SRTâVTT and serves it CORS-enabled:
# server.py
def srt_to_vtt(srt_text: str) -> str:
vtt = "WEBVTT\n\n" + srt_text.replace(",", ".")
return vtt # timestamps: 00:00:01,000 -> 00:00:01.000
@app.route("/sub/<name>.vtt")
def serve_vtt(name):
resp = make_response(open(f"subs/{{name}}.vtt").read())
resp.headers["Content-Type"] = "text/vtt"
resp.headers["Access-Control-Allow-Origin"] = "*" # iframe needs this
return resp
// player: https://vidsrc.to/embed/movie/{imdb}?sub=https://cinema.kxant.org/sub/x.vtt
Resume-play (item + server, not timestamp)
function saveResume(ids, serverIdx, s, e) {
localStorage.setItem("mycinema_resume", JSON.stringify({
ids, serverIdx, s, e, ts: Date.now()
}));
}
function getResume() {
const r = JSON.parse(localStorage.getItem("mycinema_resume") || "null");
if (!r || Date.now() - r.ts > 30*86400*1000) return null; // 30-day TTL
return r;
}
// Exact playback position is impossible: cross-origin iframes
// can't report currentTime. Item + server + episode is the best we can do.
Windows deployment gotcha
# Scheduled Tasks run as SYSTEM with a DIFFERENT PATH than your admin shell.
# A bare `python` in the task action dies instantly with no log.
# ALWAYS resolve the full exe path:
$pyExe = (Get-Command python3.14).Source
$action = New-ScheduledTaskAction -Execute $pyExe -Argument "C:\mycinema\server.py"
Register-ScheduledTask -TaskName "MyCinema" -Action $action `
-Trigger (New-ScheduledTaskTrigger -AtStartup) -User "SYSTEM" -RunLevel Highest
Hard lessons: VidSrc hard-blocks sandboxed iframes â the ad-block sandbox toggle only applies to moviesapi.to. Pre-build the catalog; live TMDB search alone returns titles no server can play.
7. n8n Notification Hub
What it is: Self-hosted n8n on Spain that watches everything â website uptime (5 min), Marzban panel reachability (15 min), systemd services (15 min), SSL expiry (daily) â and pings Telegram on problems. All alerts go through the VPN bot to the admin.
Run it capped and isolated
docker run -d --name n8n --restart unless-stopped \
--memory=1g --cpus=1 \
-p 127.0.0.1:5678:5678 \
-v n8n_data:/home/node/.n8n \
n8nio/n8n
# nginx: n8n.kbko.qzz.io -> 127.0.0.1:5678 (Let's Encrypt)
Docker-subnet DNS fix
Marzban's dnstt-server hijacks UDP/53, which also breaks DNS for Docker containers. Add a scoped exception for the Docker subnet only â VPN-client DNS stays untouched:
# Backup first!
cp /etc/iptables/rules.v4 /etc/iptables/rules.v4.bak_n8n
# Allow the Docker subnet to reach real DNS, before the hijack rule:
iptables -t nat -I PREROUTING 1 -s 172.18.0.0/16 -p udp --dport 53 -j ACCEPT
iptables-save > /etc/iptables/rules.v4
Workflow pattern â every check looks like this
{
"nodes": [
{ "name": "Every 5 min",
"type": "n8n-nodes-base.scheduleTrigger",
"parameters": { "rule": { "interval": [{ "field": "minutes", "minutesInterval": 5 }] } } },
{ "name": "Check site",
"type": "n8n-nodes-base.httpRequest",
"parameters": { "url": "https://dublab.kbko.qzz.io/health",
"options": { "timeout": 10000 } } },
{ "name": "Is it down?",
"type": "n8n-nodes-base.if",
"parameters": { "conditions": {
"number": [{ "value1": "={{$json.statusCode}}", "operation": "notEqual", "value2": 200 }] } } },
{ "name": "Alert admin",
"type": "n8n-nodes-base.telegram",
"parameters": { "chatId": "8500073574",
"text": "đ¨ dublab.kbko.qzz.io down! HTTP {{$json.statusCode}}" } }
],
"connections": {
"Every 5 min": { "main": [[{ "node": "Check site" }]] },
"Check site": { "main": [[{ "node": "Is it down?" }]] },
"Is it down?": { "main": [[{ "node": "Alert admin" }], []] }
}
}
// Same skeleton for: Marzban panel :8000 (15 min), systemd via SSH node,
// SSL expiry via `openssl s_client` in an Execute Command node (daily).
Hard lessons: For Marzban monitoring use non-authenticated reachability checks â a credentialed /api/admin/token login fires a user-visible Telegram #Login alert. Cap n8n at 1GB RAM; it will happily eat everything with enough workflows.
8. Outline Manager â Phone VPS Dashboard
What it is: A mobile-friendly Flask dashboard to run the whole VPN infrastructure from a phone â Outline keys (QR + link), Marzban users, 3xUI inbounds, SSH tunnel + SlowDNS setup, Cloudflare DNS editor, one-click installers, backup/restore. Secrets are Fernet-encrypted at rest, admin-only.
Secrets â Fernet, never plaintext
from cryptography.fernet import Fernet
import os
KEY_PATH = "/root/outline-manager/.fernet.key" # chmod 600
if not os.path.exists(KEY_PATH):
open(KEY_PATH, "wb").write(Fernet.generate_key())
os.chmod(KEY_PATH, 0o600)
fernet = Fernet(open(KEY_PATH, "rb").read())
def enc(s: str) -> str: return fernet.encrypt(s.encode()).decode()
def dec(t: str) -> str: return fernet.decrypt(t.encode()).decode()
# Stored in SQLite as enc(api_key); decrypted only in memory at request time.
# Even a DB leak reveals nothing without the key file.
Outline key â QR + link
import requests, qrcode, io, base64
def outline_keys():
api = dec(get_setting("outline_api_url")) # https://host:port/SECRET/
r = requests.get(api + "access-keys/", verify=False, timeout=15)
return r.json()["accessKeys"]
@app.route("/outline/new", methods=["POST"])
def outline_new():
api = dec(get_setting("outline_api_url"))
r = requests.post(api + "access-keys/", verify=False, timeout=15)
key = r.json()
# QR for the phone camera:
img = qrcode.make(key["accessUrl"])
buf = io.BytesIO(); img.save(buf, format="PNG")
qr_b64 = base64.b64encode(buf.getvalue()).decode()
return {"link": key["accessUrl"], "qr": qr_b64, "id": key["id"]}
Cloudflare DNS without the dashboard
The Cloudflare dashboard is bot-walled in automation environments. Skip it â the API works fine with a scoped token:
# Ask the user to create an "Edit zone DNS" token scoped to the zone,
# then call the API directly:
CF = "https://api.cloudflare.com/client/v4"
H = {"Authorization": f"Bearer {dec(get_setting('cf_token'))}"}
def dns_set(name: str, ip: str):
zone = requests.get(f"{CF}/zones?name=kxant.org", headers=H).json()["result"][0]["id"]
recs = requests.get(f"{CF}/zones/{zone}/dns_records?name={name}",
headers=H).json()["result"]
data = {"type": "A", "name": name, "content": ip, "ttl": 300, "proxied": False}
if recs:
requests.put(f"{CF}/zones/{zone}/dns_records/{recs[0]['id']}",
headers=H, json=data)
else:
requests.post(f"{CF}/zones/{zone}/dns_records", headers=H, json=data)
Hard lessons: SlowDNS needs a non-interactive installer â the form takes tunnel domain + SSH port and shows the public key, no SSH session required. Admin password changes must invalidate existing sessions.
9. Movie Scraper Bot (Telethon)
What it is: An in-bot movie scraper â searches Telegram channels for movies and serves results inside the bot. Built on Telethon (user client, not bot API) so it can read channel history.
Client setup â secrets never in code
# /root/moviebot/.tg_env (chmod 600) â the ONLY place secrets live:
# API_ID=123456
# API_HASH=abcdef123456
# (first run does interactive login, then moviebot.session persists)
from telethon import TelegramClient
import os
def load_env(path):
d = {{}}
for line in open(path):
if "=" in line and not line.startswith("#"):
k, v = line.strip().split("=", 1)
d[k] = v
return d
env = load_env("/root/moviebot/.tg_env")
client = TelegramClient("/root/moviebot/moviebot", env["API_ID"], env["API_HASH"])
async def search_movies(query: str, limit=10):
results = []
async for msg in client.iter_messages("@movie_channel", search=query, limit=limit):
if msg.video or msg.document:
results.append({{"id": msg.id, "text": msg.text or "", "date": msg.date}})
return results
Dead-token crash loop â the fix
# If the BotFather token dies, the service crash-loops every few seconds.
# That once helped OOM-freeze the whole Spain server (with musicbot).
# Guard: stop + disable until a fresh token exists.
# systemctl stop moviebot && systemctl disable moviebot
# Then paste a NEW token from @BotFather into .tg_env and re-enable.
Hard lessons: A dead token doesn't fail quietly â Telethon retries forever, eating CPU/RAM in a tight loop. Combined with zero swap, two such bots froze the entire Spain VPS. Monitor for crash-looping services, not just stopped ones.
10. OSINT Bot
What it is: A Telegram bot for quick lookups: /ip /email /user /auto /inn /all /proxycheck. Built for speed â one message in, one answer out.
Proxy handling â file format + graceful fallback
Proxies die constantly. The bot reads proxy.txt (host:port:user:pass per line) and falls back to a direct connection instead of hard-failing:
import random, requests
def load_proxies(path="/root/info/proxy.txt"):
proxies = []
for line in open(path):
line = line.strip()
if not line or line.startswith("#"):
continue
parts = line.split(":") # host:port:user:pass
if len(parts) == 4:
h, p, u, pw = parts
proxies.append(f"http://{{u}}:{{pw}}@{{h}}:{{p}}")
return proxies
PROXIES = load_proxies()
def fetch(url, timeout=15):
last_err = None
for px in random.sample(PROXIES, min(3, len(PROXIES))):
try:
return requests.get(url, proxies={{"http": px, "https": px}},
timeout=timeout).json()
except Exception as e:
last_err = e # proxy dead -> try next
# All proxies failed: direct fallback (better than nothing)
return requests.get(url, timeout=timeout).json()
Command skeleton
async def cmd_ip(update, context):
target = " ".join(context.args)
if not target:
return await update.message.reply_text("Usage: /ip 1.2.3.4")
try:
d = await asyncio.to_thread(fetch, f"http://ip-api.com/json/{{target}}")
await update.message.reply_text(
f"đ {{d.get('query')}}\n"
f"đ {{d.get('city')}}, {{d.get('country')}}\n"
f"đ˘ {{d.get('isp')}}\n"
f"đ {{d.get('timezone')}}")
except Exception as e:
await update.message.reply_text(f"â Lookup failed: {{e}}")
# Same shape for /email /user /inn â one fetcher, one formatter each.
Hard lessons: The original crash was a proxy-format mismatch (host:port:user:pass vs code expecting something else) plus missing imports â always validate the proxy file on startup and log which line failed.
11. VPN Reseller Bot
What it is: A separate wholesale panel for VPN resellers â 30% off retail, reseller manages their own customers. Terms: âĽ10 accounts/month, never below retail, WavePay in advance or weekly.
Isolation â the non-negotiable rule
# Every bot gets its OWN everything. Sharing is how outages happen.
# Token: @kbkovpnreseller_bot (different BotFather token)
# Dir: /root/resellerbot (not /root/vpnbot)
# Service: resellerbot.service (not vpnbot.service)
# DB: /root/resellerbot/reseller.db (separate SQLite)
# Admin: same human, but admin/user command namespaces never overlap
Wholesale provisioning
RESELLER_DISCOUNT = 0.30 # 30% off retail
def wholesale_price(retail_ks: int) -> int:
return int(retail_ks * (1 - RESELLER_DISCOUNT))
async def reseller_buy(update, context):
# Reseller buys at wholesale; bot creates the Marzban user
# exactly like the main bot, but tagged with reseller_id
# so reports and renewals route to the right person.
uid = update.effective_user.id
if not is_reseller(uid):
return await update.message.reply_text("â Reseller only.")
...
15-minute health watcher (cron)
# crontab -e:
*/15 * * * * /root/watchers/resellerbot-watch.sh >> /var/log/resellerbot-watch.log 2>&1
#!/bin/bash
# /root/watchers/resellerbot-watch.sh
if ! systemctl is-active --quiet resellerbot; then
MSG="đ¨ Reseller bot ááźáżááŹáážááááş\n\n⢠service áááşááąáááş\ná
á
áşááąá¸ááŤáŚá¸á"
elif [ "$(journalctl -u resellerbot --since '20 minutes ago' -o cat | grep -ci traceback)" -gt 0 ]; then
MSG="đ¨ Reseller bot ááźáżááŹáážááááş\n\n⢠Traceback error áá˝áąáˇáááş\ná
á
áşááąá¸ááŤáŚá¸á"
elif [ "$(journalctl -u resellerbot --since '20 minutes ago' -o cat | grep -c getUpdates)" -eq 0 ]; then
MSG="đ¨ Reseller bot ááźáżááŹáážááááş\n\n⢠Telegram áá˛áˇ áĄáááşááźááşááąáááş\ná
á
áşááąá¸ááŤáŚá¸á"
fi
[ -n "$MSG" ] && send_telegram "$MSG" # silent when healthy
Hard lessons: The onboarding kit (terms card + 30% pricing table + provisioning checklist + tested SpainâUSA migration steps) is reusable for every new reseller â build it once. Never mix admin, reseller and user controls.
12. Health Watchers & Automation
What it is: The monitoring layer keeping everything alive â hourly VPN health checks, 15-minute service watchers, a freeze auto-recovery, quota watchers, and a daily preventive reboot of all three servers.
The watcher pattern â 2 consecutive failures before alerting
This one rule kills 99% of false alarms. A single failed check means nothing; two in a row means something:
#!/bin/bash
# /root/watchers/vpn-server-health-watch.sh (simplified)
STATE=/root/watchers/state/health.json
URL="https://panel.kbko.qzz.io/"
was_down=$(jq -r .was_down "$STATE")
fails=$(jq -r .fails "$STATE")
if curl -sk --max-time 10 "$URL" | grep -q "Marzban"; then
fails=0
if [ "$was_down" = true ]; then
send_telegram "â
Panel ááźááşááąáŹááşá¸áá˝áŹá¸ááŤááźáŽ"
fi
was_down=false
else
fails=$((fails + 1))
if [ "$fails" -ge 2 ] && [ "$was_down" = false ]; then
# Second consecutive failure -> real alert, with fix buttons
send_telegram "đ¨ Panel down (2 checks)\n\nâ
ááźááşá፠/ âď¸ ááŹá¸áááŻááşáááş"
was_down=true
fi
fi
echo "{{\"was_down\": $was_down, \"fails\": $fails}}" > "$STATE"
# Silent when healthy. Re-alert only after 2 MORE consecutive failures.
# Fix-approval: next run fixes ONLY if the â
button was tapped.
Spain freeze auto-recovery
Standing order: "freezeááźá
áşáááŻááşá¸ áááşá¸ááŹáááşá¸á
á
áşááźáŽá¸ áááŻáĄááşáᏠáááŤáᲠááŻááşáááŻááşááŤ". Every 15 min: if both SSH and the web check fail twice in a row, reboot Spain via the IONOS panel (max once per incident), then verify and report in Burmese:
# Pseudo-logic of spain-freeze-auto-recovery (15-min cron):
ssh_ok=$(timeout 10 ssh spain "echo ok" 2>/dev/null)
web_ok=$(curl -sk --max-time 10 https://n8n.kbko.qzz.io/ -o /dev/null -w "%{{http_code}}")
if [ -z "$ssh_ok" ] && [ "$web_ok" != "200" ]; then
fails=$((fails + 1))
else
fails=0; rebooted_this_incident=false
fi
if [ "$fails" -ge 2 ] && [ "$rebooted_this_incident" = false ]; then
ionos_panel_restart "31.70.155.75" # Restart action, that server ONLY
rebooted_this_incident=true
sleep 180; verify_services # dublab, vpnbot, nginx, marzban
send_telegram "đ Spain freeze â reboot ááŻááşááźáŽá¸, service áá˝áąááźááşááąáŹááşá¸ â
"
fi
Root cause found (Oct 2025) â and fixed
# NOT a payment issue (contract clean). The real cause:
# - moviebot (dead BotFather token) + musicbot (login pending)
# crash-looped every few seconds
# - 3.8 GB RAM + ZERO swap -> memory-pressure stalls
# (panel showed RUNNING, but SSH + web were dead)
#
# Fix:
systemctl stop moviebot musicbot
systemctl disable moviebot musicbot
fallocate -l 2G /swapfile && chmod 600 /swapfile
mkswap /swapfile && swapon /swapfile
echo "/swapfile none swap sw 0 0" >> /etc/fstab # persist reboot
Daily preventive restart (07:00)
# 0 7 * * * /root/watchers/daily-restart.sh
# Spain: reboot via SSH, verify dublab/resellerbot/vpnbot/nginx/marzban
# USA: reboot via SSH, verify marzban container
# Windows: restart via IONOS panel API, verify IP + status On
# Report: one Burmese summary, silent if all green.
| Watcher | Schedule | What |
| vpn-server-health-watch | hourly | SSH + panels, throughput â¤2Ă/day, fix-approval buttons |
| resellerbot-watch | 15 min | Service + tracebacks + Telegram polling |
| spain-freeze-auto-recovery | 15 min | 2 consecutive full failures â 1 reboot max |
| dublab-gemini-quota-watch | daily 09:43 | Alert when a new Gemini key is needed |
| dublab-reviews-watch | 30 min | Alert on new 5-star reviews |
| hourly-db-replica-usa | hourly | Hot-backup 3 bot DBs Spain â USA, keep last 24 |
Hard lessons: Alerts go only to the admin Telegram ID â never to customers. No proactive watchers unless explicitly requested; the approved set is enough.
đ ď¸ Universal Patterns
Rules applied to every project, learned the hard way:
- Systemd everywhere:
Restart=always, RestartSec=5, enable --now. A service that can't survive a reboot isn't done.
- Secrets in
.env (chmod 600) â never in code, chat, logs, or screenshots. Bot tokens only in env.
- Backup before changes:
cp file{,.bak$(date +%Y%m%d)}. The 30 seconds it takes has saved hours.
- Create-and-verify before delete â especially user accounts and server migrations. New first, delete old only after the new one works.
- Inline buttons AND
/cmd routes â every bot command, both, always. No exceptions.
- Burmese-first messages â Unicode only (Zawgyi looks broken), short, Telegram-friendly.
- Free-only â every project runs on free tiers. Never suggest paid plans.
- Never break the live VPN bot â new work gets its own token, directory, service, DB, and capped resources.
- Ask before state-changing work â read-only checks are free to run; anything destructive needs approval (except the standing freeze-recovery order).
- Name the object in every closing line â a bare "ááááşáááŻááşááźáŽ" makes him ask what was closed.
1. KBKO VPN Bot â áĄáááŻáĄááťáąáŹááş VPN ááąáŹááşá¸áá˛áˇ Bot
ááŹáá˛: VPN account áĄáááŻáĄááťáąáŹááş ááąáŹááşá¸áá˛áˇ Telegram botá Customer á WavePay áá˛áˇááąá¸á screenshot áááŻáˇá admin á approve áážáááşá bot á Marzban API áááą account ááťááşááťááşá¸áá˝ááˇáşááąá¸á Marzban panel á áᯠ(Spain + USA) ááᯠááťá˝áąá¸áááşá
config.py â ááąáŹááşá¸ááťáážáŻá
ááşá¸ááťááşá¸
ááŹááąáŹááşá¸ááá˛áááŻáᏠfile áá
áşááŻáááşá¸á ááááşá¸áááşá Product ááźáąáŹááşá¸ááťááşáááş áá
áşááźáąáŹááşá¸ááźááşááŻáśáá˛:
# /root/vpnbot/config.py
SOLD_PROTOCOLS = ["vless", "shadowsocks"] # VMess/Trojan ááŹá¸ááźáŽ â áá
áşááźáąáŹááşá¸áááşá¸áá˛áˇ product ááźáąáŹááşá¸
PLANS = {
"gb30": {"gb": 30, "days": 30, "price": 3000, "label": "30GB ¡ 30 days"},
"gb100": {"gb": 100, "days": 30, "price": 5000, "label": "100GB ¡ 30 days"},
}
TRIAL_GB = 1
TRIAL_DAYS = 1
TRIAL_SERVER = "es" # trial á Spain áá˛, USA á premium ááŽá¸áááˇáş
REFERRAL_GB = 1 # áá°áááşááťááşá¸ááąáŤáşáááş +1GB
ADMIN_IDS = [8500073574]
marzban.py â token ááááşá¸áá˛áˇ API client
API ááąáŤáşáááŻááşá¸ Bearer token áááŻáááşá ááąáŤáşáááŻááşá¸ login áááşááąáááş panel á Telegram #Login alert áá˝áą áááşáááŻááşáááŻáˇáááşá ááŤááźáąáŹááˇáş áá
áşá፠login áááşááźáŽá¸ token ááᯠááááşá¸ááŹá¸á 401 ááźááşááŹáážáᏠáĄáá
áşáá°:
# /root/vpnbot/marzban.py
import requests, time
class Marzban:
def __init__(self, base_url, username, password):
self.base_url = base_url.rstrip("/")
self.username = username
self.password = password
self._token = None
def _login(self):
r = requests.post(
f"{self.base_url}/api/admin/token",
data={"username": self.username, "password": self.password},
verify=False, timeout=15)
r.raise_for_status()
self._token = r.json()["access_token"]
def _req(self, method, path, retry=True, **kw):
if not self._token:
self._login()
headers = kw.pop("headers", {})
headers["Authorization"] = f"Bearer {self._token}"
r = requests.request(method, f"{self.base_url}{path}",
headers=headers, verify=False,
timeout=20, **kw)
if r.status_code == 401 and retry:
self._login() # token áááşáááşá¸ááŻááş -> áá
áşááŤáááşáá°
return self._req(method, path, retry=False, **kw)
r.raise_for_status()
return r.json() if r.text else None
def create_user(self, username, gb, days):
# ááąáŹááşá¸ááťáážáŻá
ááşá¸ááťááşá¸: VLESS Reality + Shadowsocks áá˛á
# Panel áážáŹ VMess/Trojan áážááááşá¸ user ááᯠáááąá¸áá°á¸á
proxies = {p: {} for p in SOLD_PROTOCOLS}
inbounds = {"vless": ["VLESS TCP REALITY"],
"shadowsocks": ["Shadowsocks TCP"]}
return self._req("POST", "/api/user", json={
"username": username,
"proxies": proxies,
"inbounds": inbounds,
"data_limit": gb * 1024**3,
"expire": int(time.time()) + days * 86400,
# User áĄáá
áşáááŻááşá¸ (panel á ááŻááŻáśá¸ á
ááşá¸ááźáŽá¸ááŹá¸):
"flow": "xtls-rprx-vision", # VLESS Reality flow
})
# NOTE: Shadowsocks method aes-256-gcm ááᯠpanel inbound áážáŹáááş áááşáážááşá
bot.py â server áá˝áąá¸áá˛áˇ buy flow
Command áááŻááşá¸áážáŹ inline button ááąáŹ /cmd á፠á ááŻááŻáśá¸ áĄááŻááşááŻááşááááş:
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.ext import ContextTypes
async def buy(update: Update, context: ContextTypes.DEFAULT_TYPE):
kb = [
[InlineKeyboardButton("đşđ¸ USA ¡ Premium â", callback_data="srv_us")],
[InlineKeyboardButton("đŞđ¸ Spain", callback_data="srv_es")],
]
await update.message.reply_text(
"Server áá˝áąá¸á፠/ Choose server:",
reply_markup=InlineKeyboardMarkup(kb))
async def on_server(update: Update, context: ContextTypes.DEFAULT_TYPE):
q = update.callback_query
await q.answer()
server = q.data.replace("srv_", "") # "us" áááŻáˇáááŻááş "es"
context.user_data["server"] = server
kb = [[InlineKeyboardButton(p["label"], callback_data=f"plan_{code}")]
for code, p in PLANS.items()]
await q.edit_message_text("Plan áá˝áąá¸ááŤ:",
reply_markup=InlineKeyboardMarkup(kb))
bot.py â WavePay screenshot â áá
áşááťááşáážáááş approve
áá
áşáááşáĄáááŻáĄááťáąáŹááş: customer á screenshot áááŻáˇá admin á Approve áážáááşá account ááťááşááťááşá¸áá˝ááˇáş:
async def on_payment_photo(update: Update, context: ContextTypes.DEFAULT_TYPE):
uid = update.effective_user.id
plan = context.user_data.get("plan")
photo = update.message.photo[-1].file_id
kb = [[InlineKeyboardButton("â
Approve",
callback_data=f"appr_{uid}_{plan}")]]
await context.bot.send_photo(
ADMIN_IDS[0], photo,
caption=f"đ° Payment from {uid} ¡ plan {plan}",
reply_markup=InlineKeyboardMarkup(kb))
await update.message.reply_text("âł Admin á
á
áşááąáááş, ááá
áąáŹááˇáşâŚ")
async def on_approve(update: Update, context: ContextTypes.DEFAULT_TYPE):
q = update.callback_query
await q.answer()
_, uid, plan = q.data.split("_", 2)
server = user_server(uid)
mz = Marzban(**PANELS[server]) # Spain áááŻáˇáááŻááş USA panel
p = PLANS[plan]
try:
u = mz.create_user(f"u{uid}", p["gb"], p["days"])
save_order(uid, plan, server) # SQLite: users/orders table
await context.bot.send_message(
int(uid),
f"â
áááźáŽ!\n\n{p['label']}\n\n"
f"Subscription:\n<code>{u['subscription_url']}</code>\n\n"
f"Happ / Hiddify áá˛áááˇáş, connect áážáááş â áááźáŽ đ",
parse_mode="HTML")
await q.edit_message_caption(caption="â
Approved & created")
except Exception as e:
await q.edit_message_caption(caption=f"â Failed: {e}")
Deploy ááŻááşáááşá¸
# Systemd â crash ááźá
áşáááşá¸ áá°áˇááŹáá° ááźááşá (áá°á
áąáŹááˇáşá
ááŹááááŻ)
sudo tee /etc/systemd/system/vpnbot.service << 'EOF'
[Unit]
Description=KBKO VPN Bot
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/root/vpnbot
ExecStart=/root/vpnbot/venv/bin/python bot.py
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload && sudo systemctl enable --now vpnbot
áááşáááşá¸á
áŹááťáŹá¸: Marzban á /api/admin/token áĄáąáŹááşáááŻááşá¸ Telegram #Login alert áááŻáˇáááş â bot admin áá˛áˇ telegram_id ááᯠpanel DB áážáŹ NULL ááŻááşáá
áşá ááŻááşá¸ spam áááźá
áşááąáŹáˇáá°á¸á áááŻááˇáş human admin áá˛áˇ telegram_id áááŻááąáŹáˇ ááťááşááŹá¸ (áááŻááş login áááşáááş alert ááŹáŚá¸áááş)á Monitoring áĄáá˝ááş login ááááŻáá˛áˇ á
á
áşááąá¸áážáŻáá˛ááŻáśá¸á
2. Marzban VPN Infrastructure (Spain + USA)
ááŹáá˛: Marzban panel á áᯠ(Spain 31.70.155.75, USA 198.251.70.226) â VLESS Reality áá˛áˇ Shadowsocks ááᯠXray-core áá˛áˇááąá¸á iptables áá˛áˇ ááŻáśááźáŻáśáĄáąáŹááşááŻááşá subscription áĄáá˝ááş nginx ááśááŹá¸á
VLESS ááᯠááŹáááŻáˇ port 443 ááźáąáŹááşá¸ááŹáá˛
Atom mobile data áá˛áˇ á
ááşá¸ááŹ: Shadowsocks :1080 ááááşá VLESS :2053, VMess :2080, Trojan :2081 áĄááŻááş timeoutá Carrier á port áá°á¸áá°á¸áááşá¸áááşá¸áá˝áą ááááşáááşá 443 (HTTPS) ááᯠáááşááąáŹáˇááž áááááşáá°á¸:
# USA panel host ááąáŤáşáážáŹ â áĄáááşááŻáśá¸ backup áá° (áááźá
áşáááą)
cp /var/lib/marzban/xray_config.json /var/lib/marzban/xray_config.json.bak2053
# Marzban panel áá˛áážáŹ: inbounds -> VLESS TCP REALITY -> port 2053 -> 443
cd /opt/marzban && docker compose restart marzban
# xray ááááş 443 ááŹá¸ááąáŹááşááąááŹá¸ á
á
áş
ss -tlnp | grep ':443' # xray áááŻáˇ ááąáŤáşááááş
# Customer áĄááąáŹááşá¸áá˝áą subscription refresh ááŻááşááž port áĄáá
áşááááşá
Firewall â áá
áşááąáŹááş IP áááşá¸ááᲠpanel áááşáááŻáˇá
Marzban dashboard (:8000) ááᯠSpain server ááᲠáááşáááŻáˇáá ááťááşáᏠkernel level áážáŹáááş áá
áş:
# USA Marzban host ááąáŤáşáážáŹ run
iptables -A INPUT -i lo -j ACCEPT # localhost áĄááźá˛
iptables -A INPUT -p tcp --dport 8000 -s 31.70.155.75 -j ACCEPT # Spain áá˛
iptables -A INPUT -p tcp --dport 8000 -j DROP # ááťááş áĄááŻááş
iptables-save > /etc/iptables/rules.v4 # reboot áááşá¸ áááťááş
nginx â VPN ááááŻáᲠsubscription ááąá¸
ááźáżááŹ: customer á VPN áááşááźáŽá¸ subscription link áá°áááŻáˇ VPN áĄáááşááťáąáŹáşááááş (:8000 ááąáŤáşáážáŹ á ááŻááŻáśá¸áážááááŻáˇ)á áĄááźáą: nginx :8443 á /sub/* áᲠMarzban áᎠáááŻáˇ; /, /dashboard, /api áĄááŻááş 404á ááá: Marzban áá˛áˇ :8000 á HTTPS (self-signed) áááŻáˇ proxy á verify ááááşááááş:
# /etc/nginx/sites-available/mm-sub-proxy
server {{
listen 8443 ssl;
server_name mm.kxant.eu.cc;
ssl_certificate /etc/letsencrypt/live/mm.kxant.eu.cc/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mm.kxant.eu.cc/privkey.pem;
location /sub/ {{
proxy_pass https://127.0.0.1:8000; # Marzban á HTTPS (self-signed)
proxy_ssl_verify off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}}
location / {{ return 404; }} # dashboard á Spain áá˛
}}
# subscription URL áĄáá
áşáá˝áą port áĄáá˝ááˇáşááᯠáá˝ážááşá¸
# /opt/marzban/.env:
XRAY_SUBSCRIPTION_URL_PREFIX="https://mm.kxant.eu.cc:8443"
# ááá: `docker compose restart` á .env áĄááźáąáŹááşá¸ááᯠááááşáá°á¸á
# Container áĄáá
áşááźááşáááş:
cd /opt/marzban && docker compose up -d
docker exec marzban-marzban-1 env | grep XRAY_SUBSCRIPTION_URL_PREFIX
Bot login Telegram spam ááááş
Marzban á admin login áááŻááşá¸ ááťáááşááŹá¸áá˛áˇ Telegram ID ááᯠnotify áááŻáˇáááş â bot á áááá login áááşáááşá bot admin áá˛áˇááŹáᲠááźáŻááşá áá°áá˛áˇáᏠááťááşááŹá¸:
python3 -c "
import sqlite3
c = sqlite3.connect('/var/lib/marzban/db.sqlite3')
c.execute(\"UPDATE admins SET telegram_id = NULL WHERE username = 'vpnbot'\")
c.commit()
print(list(c.execute('SELECT username, telegram_id FROM admins')))
"
# -> [('kbko', 8500073574), ('vpnbot', None)]
# Bot login: ááááşááááşá áááŻááş login áááşáááş: alert ááŹáŚá¸áááşá
áááşáááşá¸á
áŹááťáŹá¸: Inbound ááźááşáááş cp xray_config.json{,.bak} áĄááźá˛ááŻááşá docker compose restart á .env ááᯠááááşáá°á¸ â up -d ááŻáśá¸á Port á
ááşá¸áááş server ááąáŤáşááááŻááşáᲠuser áá˛áˇ ááááˇáş network (Atom) áááą á
ááşá¸ â server áááşá á
á
áşáᏠcarrier filtering ááᯠáááźáá°á¸á
3. PyHost Bot â áĄááá˛áˇ Hosting Platform
ááŹáá˛: User áá˝áąááᯠVPS ááąáŤáşáážáŹ áĄááá˛áˇ Python hosting ááąá¸áá˛áˇ botá Script deploy ááŻááşá áá
áşááŻááťááşá¸ááᯠresource áááˇáşáááşááŹá¸áá˛áˇ systemd service áá˛áˇ áááşá Premium tier áá˛áˇ trial key áá˝áąááŤá
Deploy ááŻáśá
áś â user áá
áşááŻááᯠsystemd service áá
áşááŻ
Isolation á ááŽáááŻááşá¸áĄááá: script áá
áşááŻáá°á¸áá˝áŹá¸áááşá¸ áá°áˇ service áá˛ááąá host áá˛áˇ VPN bot ááᯠááááá°á¸á Resource áááˇáşáááşáᏠáááźá
áşáááą:
import subprocess, textwrap
def deploy_script(user_id: int, filename: str):
svc = f"pyhost-{{user_id}}"
unit = textwrap.dedent(f"""
[Unit]
Description=PyHost user {{user_id}}
After=network.target
[Service]
Type=simple
User=nobody
WorkingDirectory=/root/pyhostbot/apps/{{user_id}}
ExecStart=/usr/bin/python3 {{filename}}
Restart=on-failure
# --- áááˇáşáááş: user áá
áşááąáŹááş server ááᯠáááşááąáŹáˇááž ááá˛áááŻááş ---
MemoryMax=512M
CPUQuota=100%
[Install]
WantedBy=multi-user.target
""")
open(f"/etc/systemd/system/{{svc}}.service", "w").write(unit)
subprocess.run(["systemctl", "daemon-reload"], check=True)
subprocess.run(["systemctl", "enable", "--now", svc], check=True)
Trial key â PH-XXXXXXXX, áá
áşááąáŹááş áá
áşááŤ
import secrets, sqlite3, time
def gen_key(days: int, max_uses: int, note: str = "") -> str:
key = "PH-" + secrets.token_hex(4).upper() # áĽááᏠPH-9F3A2C1D
db.execute("INSERT INTO trial_keys(key,days,max_uses,used,note) VALUES(?,?,?,?,?)",
(key, days, max_uses, 0, note))
db.commit()
return key
def redeem_key(user_id: int, key: str) -> str:
row = db.execute("SELECT days,max_uses,used FROM trial_keys WHERE key=?",
(key,)).fetchone()
if not row: return "â Key ááážááşáá°á¸"
if row["used"] >= row["max_uses"]: return "â áᎠkey ááŻááşáá˝áŹá¸ááźáŽ"
if db.execute("SELECT 1 FROM redemptions WHERE key=? AND user_id=?",
(key, user_id)).fetchone():
return "â áááş áᎠkey ááŻáśá¸ááźáŽá¸ááźáŽ (áá
áşááąáŹááş áá
áşááŤáá˛)"
db.execute("UPDATE trial_keys SET used=used+1 WHERE key=?", (key,))
db.execute("INSERT INTO redemptions(key,user_id) VALUES(?,?)", (key, user_id))
db.execute("INSERT OR REPLACE INTO premiums(user_id,plan,expire) VALUES(?,?,?)",
(user_id, "pro", int(time.time()) + row["days"] * 86400))
db.commit()
return f"â
{{row['days']}} áááş Pro áááźáŽ!"
/admin panel â áážáááşááŻáś button áá˝áą
async def admin_panel(update, context):
kb = [
[InlineKeyboardButton("đ Plans", callback_data="adm_plans")],
[InlineKeyboardButton("đ Premiums", callback_data="adm_premiums")],
[InlineKeyboardButton("đ Keys", callback_data="adm_keys")],
[InlineKeyboardButton("â /addpremium", callback_data="adm_addpremium")],
]
await update.message.reply_text("Admin panel:",
reply_markup=InlineKeyboardMarkup(kb))
# á
ááşá¸ááťááşá¸: "adm_X" button áááŻááşá¸á /X áá˛áˇ function áĄáá°áá°áᲠááąáŤáşááááşá
áááşáááşá¸á
áŹááťáŹá¸: Premium â admin â premium user ááąáŹááş free deploy/renew á admin áá˛ááŻááşáááŻáˇáá Deployment áá
áşááŻááťááşá¸ memory áááˇáşáááşááŹá¸á loop áĄááŻáśá¸ááážá áá
áşáᯠVPS áá
áşááŻááŻáśá¸ OOM ááźá
áşáááşá Fake test double á real library áá˛áˇ API áá˛áˇ áĄáááĄááť áá°ááááşá ááá°áááş test áĄáąáŹááş production crashá
4. VidDrop â Video Downloader Bot
ááŹáá˛: Video link áááŻáˇ â quality button + MP3 â file á chat áá˛ááąáŹááşá yt-dlp ááŻáśá¸á áá
áşááźááŻááşáááş 2 ááŻáᲠdownload ááŻááş (VPS ááá)á
Format list â inline button
import yt_dlp, asyncio
def list_formats(url: str):
with yt_dlp.YoutubeDL({{"quiet": True}}) as ydl:
info = ydl.extract_info(url, download=False)
seen, out = set(), []
for f in info.get("formats", []):
h = f.get("height")
if h and h not in seen and h <= 1080:
seen.add(h)
out.append((h, f["format_id"]))
return info.get("title", "video"), sorted(out)
async def on_link(update, context):
title, fmts = await asyncio.to_thread(list_formats, update.message.text)
kb = [[InlineKeyboardButton(f"đŹ {{h}}p", callback_data=f"dl_{{fid}}_{{h}}")]
for h, fid in fmts]
kb.append([InlineKeyboardButton("đľ MP3", callback_data="dl_mp3")])
context.user_data["dl_url"] = update.message.text
await update.message.reply_text(f"{{title}}\nQuality áá˝áąá¸ááŤ:",
reply_markup=InlineKeyboardMarkup(kb))
Semaphore áá˛áˇ download (áĄááťáŹá¸ 2 ááŻ)
SEMAPHORE = asyncio.Semaphore(2)
async def on_quality(update, context):
q = update.callback_query
await q.answer()
url = context.user_data["dl_url"]
async with SEMAPHORE: # 2 ááŻáááş ááááŻ
if q.data == "dl_mp3":
opts = {{"format": "bestaudio/best",
"postprocessors": [{{"key": "FFmpegExtractAudio",
"preferredcodec": "mp3"}}]}}
else:
_, fid, h = q.data.split("_")
opts = {{"format": fid, "max_filesize": 50 * 1024 * 1024}}
opts.update({{"outtmpl": "/tmp/viddrop/%(id)s.%(ext)s", "quiet": True}})
fname = await asyncio.to_thread(download, url, opts)
await q.message.reply_document(open(fname, "rb"))
os.remove(fname) # disk áááźááˇáş
def download(url, opts):
with yt_dlp.YoutubeDL(opts) as ydl:
info = ydl.extract_info(url, download=True)
return ydl.prepare_filename(info)
áááşáááşá¸á
áŹááťáŹá¸: TikTok á datacenter IP ááááşáááş â áĄááźáąá user áá˛áˇ áááŻááşáááŻááş login cookies.txt ááᯠ--cookies áá˛áˇááąá¸; cookie áĄááźáąáŹááşá¸ áááşááąáŹáˇááž áááŻááşá 50MB á Telegram bot limit â download áááŻááşáááş max_filesize áááˇáşáááşá
5. DubLab â AI Video Dubbing Website
ááŹáá˛: Video áááş (á
áááá
áşáĄáá / 250MB) â ENâMM + áĄááśáá˝áąá¸ â dub ááŹá¸áá˛áˇ MP4 áá Pipeline: faster-whisper á
áŹááŹá¸ááŻááş â Gemini ááŹááŹááźááş â Edge-TTS áĄááśáá˝ááş (ááźááşááŹáĄááśááááş!) â FFmpeg ááąáŤááşá¸á SRT translator tab ááŤá
áĄáááˇáş 1 â faster-whisper áá˛áˇ á
áŹááŹá¸ááŻááş
from faster_whisper import WhisperModel
model = WhisperModel("small", device="cpu", compute_type="int8")
def transcribe(path: str):
segments, _ = model.transcribe(path, language="en")
return [{{"start": s.start, "end": s.end, "text": s.text.strip()}}
for s in segments if s.text.strip()]
# áĄááŻááşááźá
áşáá˛áˇ version áĄáá˝á˛ (faster-whisper 1.2.1):
# av==15.1.0 + huggingface_hub<1
# av 19.x á faster-whisper ááŻáśá¸áá˛áˇ `metadata_errors` ááᯠááźáŻááşáá
áş -> TypeErrorá
# av 14.x áážáŹ cp312 wheel ááážá -> build ááťááşá 15.1.0 á áĄááąáŹááşá¸ááŻáśá¸á
áĄáááˇáş 2 â Gemini áá˛áˇ ááŹááŹááźááş (key áĄáážááˇáşááť)
Gemini áĄááá˛áˇ quota á ááąá¸áááş (~áá
áşááąáˇ 10â15 req/key â 1â2 video)á Key áá˝áą áĄáážááˇáşááťááŻáśá¸á áá
áşááŻááźááˇáşáááş ááąáŹááşáá
áşááŻ:
import google.generativeai as genai, itertools
KEYS = open("/root/dublab/.env").read().split("GEMINI_API_KEYS=")[1].split(",")
key_cycle = itertools.cycle(k.strip() for k in KEYS)
def translate(text: str, target: str) -> str:
last_err = None
for _ in range(len(KEYS)): # key áá
áşááŻááťááşá¸ áá
áşááŤá
ááşá¸ďż˝
genai.configure(api_key=next(key_cycle))
try:
m = genai.GenerativeModel("gemini-2.0-flash")
r = m.generate_content(
f"Translate to {{target}}, return ONLY the translation: {{text}}")
return r.text.strip()
except Exception as e:
last_err = e # quota ááźááˇáş -> ááąáŹááş key
raise last_err
áĄáááˇáş 3 â Edge-TTS áá˛áˇ áĄááśáá˝ááş (ááźááşááŹáĄááś!)
import edge_tts, asyncio
VOICES = {{
"mm-male": "my-MM-ThihaNeural",
"mm-female": "my-MM-NilarNeural",
"en-male": "en-US-ChristopherNeural",
"en-female": "en-US-AriaNeural",
}}
async def speak(text: str, voice: str, out: str):
await edge_tts.Communicate(text, voice).save(out)
# áĄáááŻááşá¸áá
áşááŻááťááşá¸ áĄááśáá˝ááşá ááźáŽá¸áááş timestamp window áá˛áˇ áááŻááşáĄáąáŹááş
# FFmpeg `atempo` áá˛áˇ áá˝á˛/áážá
áş â áážáŻááşáááşá¸áážáŻááşáá˛áˇ áááˇáşáážááşá¸áá°á
áĄáááˇáş 4 â FFmpeg áá˛áˇ ááąáŤááşá¸
# dubbed.mp3 = áĄáááŻááşá¸áááŻááş clip áá˝áąáááş, áá°áááşá¸áĄááś áááŻá¸áááŻá¸ááąá¸ááťááşááŹá¸
subprocess.run([
"ffmpeg", "-y",
"-i", "original.mp4",
"-i", "dubbed.mp3",
"-filter_complex",
"[0:a]volume=0.25[orig];[1:a]adelay=0[db];[orig][db]amix=inputs=2[a]",
"-map", "0:v", "-map", "[a]",
"-c:v", "copy", "-c:a", "aac",
"output.mp4"], check=True)
Serve ááŻááşááŻáś â nginx ááąáŹááşá waitress, iOS áĄáá˝ááş
# waitress ááᯠ127.0.0.1 áá˛áá˝ááˇáş (app server ááᯠáááŻááşáááŻááşáááź)
# /etc/systemd/system/dublab.service:
# ExecStart=/root/dublab/venv/bin/waitress-serve --listen=127.0.0.1:5000 app:app
/* iOS Safari bug: FormData áá˛áˇ fetch() POST áááşáááş
"TypeError: The string did not match the expected pattern" (WebKit bug)á
áĄááźáą: upload ááᯠXMLHttpRequest ááŻáśá¸, fetch á JSON polling áá˛á */
function uploadFile(file) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
const fd = new FormData();
fd.append("video", file);
xhr.open("POST", "/api/dub");
xhr.onload = () => resolve(JSON.parse(xhr.responseText));
xhr.onerror = reject;
xhr.send(fd);
});
}
áááşáááşá¸á
áŹááťáŹá¸: Job áá˝áą memory áá˛áážáŹáá˛áážááááş â service restart ááŻááşáááş job ID áĄááąáŹááşá¸ ááťááşá Upload áááˇáşáááş (IP áá
áşáᯠá ááŹáᎠ10 ááŻ)á áááŻááşáááş user áá
áşááąáŹááş Gemini quota áĄááŻááşá
áŹá¸á Monitor á ááááşááŹá¸áá˛áˇ site ááᯠáá°áˇááŹáá° ááźááşáá˝ááˇáşáááŻáˇ ááá
6. MyCinema â ááŻááşáážááşááźááˇáşáá˛áˇ Website
ááŹáá˛: Windows VPS ááąáŤáşá "ááźááˇáşááŻáśáááşáááş" streaming site â áážáŹá server áá˝áąá¸á ááťááşááťááşá¸ááźááˇáşá ááŻááşáážááş 6,487 + TV 1,330á áĄáŹá¸ááŻáśá¸ ááźááˇáşáááŻáˇáá Download link ááážáá
Player â server URL ááŻáśá
áśáá˝áą
áĄááá server á áᯠ+ MoviesHUB server 229 ááŻá App á iframe src áᲠáá˛ááŹ:
const SERVERS = [
{{ name: "Server 1 ¡ VidSrc", movie: id => `https://vidsrc.to/embed/movie/${{id.imdb}}`,
tv: (id,s,e) => `https://vidsrc.to/embed/tv/${{id.imdb}}/${{s}}/${{e}}` }},
{{ name: "Server 2 ¡ MoviesAPI", movie: id => `https://moviesapi.to/movie/${{id.tmdb}}`,
tv: (id,s,e) => `https://moviesapi.to/tv/${{id.tmdb}}-${{s}}-${{e}}` }},
{{ name: "Server 3 ¡ VidLink", movie: id => `https://vidlink.pro/movie/${{id.tmdb}}`,
tv: (id,s,e) => `https://vidlink.pro/tv/${{id.tmdb}}/${{s}}/${{e}}` }},
];
function play(ids, serverIdx = 0, season = 1, ep = 1) {{
const s = SERVERS[serverIdx];
const url = ids.imdb && serverIdx === 0
? s.movie(ids) : s.tv(ids, season, ep);
document.getElementById("player").src = url;
saveResume(ids, serverIdx, season, ep); // localStorage, áááş 30 áááşáááşá¸
}}
TMDB áá˛áˇ áážáŹ
const TMDB = "https://api.themoviedb.org/3";
async function search(q) {{
const r = await fetch(`${{TMDB}}/search/multi?api_key=${{API_KEY}}&query=${{encodeURIComponent(q)}}`);
const j = await r.json();
return j.results.filter(x => x.media_type === "movie" || x.media_type === "tv");
}}
// Catalog ááᯠááźááŻáááşááŹá¸ (movies.json / tvshows.json) â
// server áá
áşááŻáᯠááźááˇáşáááŻáˇááá˛áˇ title áá˝áąáᲠáááˇáşá
SRT subtitle â áááşá ááźáąáŹááşá¸á CORS áá˛áˇ ááąá¸
VidSrc á VTT URL ááąá¸áááş subtitle áááşááśáááşá Server á SRTâVTT ááźáąáŹááşá¸á CORS áá˝ááˇáşááąá¸:
# server.py
def srt_to_vtt(srt_text: str) -> str:
vtt = "WEBVTT\n\n" + srt_text.replace(",", ".")
return vtt # 00:00:01,000 -> 00:00:01.000
@app.route("/sub/<name>.vtt")
def serve_vtt(name):
resp = make_response(open(f"subs/{{name}}.vtt").read())
resp.headers["Content-Type"] = "text/vtt"
resp.headers["Access-Control-Allow-Origin"] = "*" # iframe áĄáá˝ááş áááŻ
return resp
// player: https://vidsrc.to/embed/movie/{imdb}?sub=https://cinema.kxant.org/sub/x.vtt
áááşááźááˇáş (item + server, áĄááťáááşáááŻááş)
function saveResume(ids, serverIdx, s, e) {
localStorage.setItem("mycinema_resume", JSON.stringify({
ids, serverIdx, s, e, ts: Date.now()
}));
}
function getResume() {
const r = JSON.parse(localStorage.getItem("mycinema_resume") || "null");
if (!r || Date.now() - r.ts > 30*86400*1000) return null; // áááş 30
return r;
}
// ááááťáá˛áˇ áĄááťáááşáááá°á¸: cross-origin iframe á currentTime áááźáąáŹááźá
Windows deploy ááá
# Scheduled Task áá˝áąá SYSTEM áĄááąáá˛áˇ run â PATH á admin shell áá˛áˇ ááá°á
# `python` áĄáááŻááąáŹááş task áá˛áááˇáşáááş ááááşááááşááąá
# python exe path áĄááźááˇáş áĄááźá˛ááŻáśá¸:
$pyExe = (Get-Command python3.14).Source
$action = New-ScheduledTaskAction -Execute $pyExe -Argument "C:\mycinema\server.py"
Register-ScheduledTask -TaskName "MyCinema" -Action $action `
-Trigger (New-ScheduledTaskTrigger -AtStartup) -User "SYSTEM" -RunLevel Highest
áááşáááşá¸á
áŹááťáŹá¸: VidSrc á sandboxed iframe ááᯠááŻáśá¸áááááşáááş â ad-block sandbox á moviesapi.to áĄáá˝ááşáá˛á Catalog ááᯠááźááŻáááş; TMDB live search áá˛ááᯠserver ááźááˇáşáááá˛áˇ title áá˝áą ááŤááŹáááşá
7. n8n Notification Hub
ááŹáá˛: Spain ááąáŤáşá áááŻááşáááŻááş n8n â áĄááŹáĄáŹá¸ááŻáśá¸ á
áąáŹááˇáşááźááˇáş: website (á
áááá
áş), Marzban panel (15 áááá
áş), systemd service (15 áááá
áş), SSL áááşáááşá¸ (ááąáˇáááŻááşá¸) â ááźáżááŹáážááááş Telegram áááŻáˇá Alert áĄáŹá¸ááŻáśá¸ VPN bot áááą admin ááŽá
RAM áááˇáşáááşááźáŽá¸ run
docker run -d --name n8n --restart unless-stopped \
--memory=1g --cpus=1 \
-p 127.0.0.1:5678:5678 \
-v n8n_data:/home/node/.n8n \
n8nio/n8n
# nginx: n8n.kbko.qzz.io -> 127.0.0.1:5678 (Let's Encrypt)
Docker DNS ááźáżááŹáĄááźáą
Marzban áá˛áˇ dnstt-server á UDP/53 ááᯠáááŻá¸áááş â Docker container áá˝áąá፠DNS ááťááşá Docker subnet ááŽá¸áááˇáş ááźá˝ááşá¸ááťááşááąá¸ â VPN client DNS ááᯠááá:
# áĄáááş backup!
cp /etc/iptables/rules.v4 /etc/iptables/rules.v4.bak_n8n
# Docker subnet ááᯠDNS áĄá
á
áşáá˝áŹá¸ áá˝ááˇáşááźáŻ (hijack rule áĄáááş):
iptables -t nat -I PREROUTING 1 -s 172.18.0.0/16 -p udp --dport 53 -j ACCEPT
iptables-save > /etc/iptables/rules.v4
Workflow ááŻáśá
áś â á
á
áşááąá¸áážáŻáááŻááşá¸ ááŽááŻáśáá˛
{
"nodes": [
{ "name": "Every 5 min",
"type": "n8n-nodes-base.scheduleTrigger",
"parameters": { "rule": { "interval": [{ "field": "minutes", "minutesInterval": 5 }] } } },
{ "name": "Check site",
"type": "n8n-nodes-base.httpRequest",
"parameters": { "url": "https://dublab.kbko.qzz.io/health",
"options": { "timeout": 10000 } } },
{ "name": "Is it down?",
"type": "n8n-nodes-base.if",
"parameters": { "conditions": {
"number": [{ "value1": "={{$json.statusCode}}", "operation": "notEqual", "value2": 200 }] } } },
{ "name": "Alert admin",
"type": "n8n-nodes-base.telegram",
"parameters": { "chatId": "8500073574",
"text": "đ¨ dublab.kbko.qzz.io down! HTTP {{$json.statusCode}}" } }
],
"connections": {
"Every 5 min": { "main": [[{ "node": "Check site" }]] },
"Check site": { "main": [[{ "node": "Is it down?" }]] },
"Is it down?": { "main": [[{ "node": "Alert admin" }], []] }
}
}
// ááŽááŻáśá
áśáĄáááŻááşá¸: Marzban panel :8000 (15 áááá
áş), SSH node áá˛áˇ systemd,
// Execute Command node áᲠ`openssl s_client` áá˛áˇ SSL (ááąáˇáááŻááşá¸)á
áááşáááşá¸á
áŹááťáŹá¸: Marzban á
áąáŹááˇáşááźááˇáşáᏠlogin ááááŻáá˛áˇ áĄááąá¸á
á
áşááąá¸áážáŻáá˛ááŻáśá¸ â /api/admin/token áá˛áˇ login áááşáááş user ááźááşáá˛áˇ Telegram #Login alert áá˝ááşá n8n ááᯠ1GB áááˇáşáááşá workflow ááťáŹá¸áááş RAM áĄááŻááşá
áŹá¸á
8. Outline Manager â ááŻááşá¸áá˛áˇ VPS á
áŽááśáááˇáşáá˝á˛áážáŻ
ááŹáá˛: ááŻááşá¸áá˛áˇ VPN infrastructure áĄáŹá¸ááŻáśá¸ á
áŽááśáá˛áˇ Flask dashboard â Outline key (QR + link), Marzban user, 3xUI, SSH tunnel + SlowDNS, Cloudflare DNS, áá
áşááťááşáážáááş install, backup/restoreá Secret áá˝áą Fernet áá˛áˇ encryptá admin ááŽá¸áááˇáşá
Secret â Fernet, plaintext ááŻáśá¸ááááŹá¸
from cryptography.fernet import Fernet
import os
KEY_PATH = "/root/outline-manager/.fernet.key" # chmod 600
if not os.path.exists(KEY_PATH):
open(KEY_PATH, "wb").write(Fernet.generate_key())
os.chmod(KEY_PATH, 0o600)
fernet = Fernet(open(KEY_PATH, "rb").read())
def enc(s: str) -> str: return fernet.encrypt(s.encode()).decode()
def dec(t: str) -> str: return fernet.decrypt(t.encode()).decode()
# SQLite áá˛áážáŹ enc(api_key) áá˛ááááşá¸; request ááŹááž memory áá˛áážáŹ ááźááşá
# DB ááąáŤááşááźáŹá¸áááşá¸ key file ááážááááş ááŹáážááá
Outline key â QR + link
import requests, qrcode, io, base64
def outline_keys():
api = dec(get_setting("outline_api_url")) # https://host:port/SECRET/
r = requests.get(api + "access-keys/", verify=False, timeout=15)
return r.json()["accessKeys"]
@app.route("/outline/new", methods=["POST"])
def outline_new():
api = dec(get_setting("outline_api_url"))
r = requests.post(api + "access-keys/", verify=False, timeout=15)
key = r.json()
img = qrcode.make(key["accessUrl"]) # ááŻááşá¸ camera áá˛áˇ scan
buf = io.BytesIO(); img.save(buf, format="PNG")
qr_b64 = base64.b64encode(buf.getvalue()).decode()
return {{"link": key["accessUrl"], "qr": qr_b64, "id": key["id"]}}
Cloudflare DNS â dashboard ááááŻáá°á¸
Cloudflare dashboard á automation áážáŹ bot-wall áááááşá ááťáąáŹáş â API á scoped token áá˛áˇ áĄááŻááşááźá
áş:
# User ááᯠ"Edit zone DNS" token (zone scope) áááŻááşá¸ááŻááş,
# ááźáŽá¸áááş API áááŻááşáááŻááşááąáŤáş:
CF = "https://api.cloudflare.com/client/v4"
H = {{"Authorization": f"Bearer {{dec(get_setting('cf_token'))}}"}}
def dns_set(name: str, ip: str):
zone = requests.get(f"{{CF}}/zones?name=kxant.org", headers=H).json()["result"][0]["id"]
recs = requests.get(f"{{CF}}/zones/{{zone}}/dns_records?name={{name}}",
headers=H).json()["result"]
data = {{"type": "A", "name": name, "content": ip, "ttl": 300, "proxied": False}}
if recs:
requests.put(f"{{CF}}/zones/{{zone}}/dns_records/{{recs[0]['id']}}",
headers=H, json=data)
else:
requests.post(f"{{CF}}/zones/{{zone}}/dns_records", headers=H, json=data)
áááşáááşá¸á
áŹááťáŹá¸: SlowDNS ááᯠnon-interactive installer ááŻáśá¸ â form áážáŹ tunnel domain + SSH port ááźááˇáşá public key ááźááşá SSH session ááááŻá Admin password ááźáąáŹááşá¸áááş session áĄááąáŹááşá¸áá˝áą ááťááşááááşá
9. Movie Scraper Bot (Telethon)
ááŹáá˛: Bot áá˛áááą ááŻááşáážáŹáá˛áˇ scraper â Telegram channel áá˝áąáážáŹ ááŻááşáážáŹááźáŽá¸ bot áá˛áᲠááźááşá Telethon (user client, bot API áááŻááş) ááŻáśá¸áááŻáˇ channel history áááşáááŻáˇáá
Client áááş â secret ááᯠcode áᲠááŻáśá¸áááááˇáş
# /root/moviebot/.tg_env (chmod 600) â secret áážááá˛áˇ áá
áşááąááŹáááşá¸:
# API_ID=123456
# API_HASH=abcdef123456
# (ááááĄááźáááş interactive login, ááźáŽá¸áááş moviebot.session ááááşá¸)
from telethon import TelegramClient
def load_env(path):
d = {{}}
for line in open(path):
if "=" in line and not line.startswith("#"):
k, v = line.strip().split("=", 1)
d[k] = v
return d
env = load_env("/root/moviebot/.tg_env")
client = TelegramClient("/root/moviebot/moviebot", env["API_ID"], env["API_HASH"])
async def search_movies(query: str, limit=10):
results = []
async for msg in client.iter_messages("@movie_channel", search=query, limit=limit):
if msg.video or msg.document:
results.append({{"id": msg.id, "text": msg.text or "", "date": msg.date}})
return results
Token ááąáááş crash loop â áĄááźáą
# BotFather token ááąáááş service á á
áášáááˇáşáĄáááşá¸áááşááźáŹá¸ crash-loop áááşá
# ááŤá Spain server áá
áşááŻááŻáśá¸ freeze ááźá
áşá
áąáá˛áˇáá˛áˇ áĄááźáąáŹááşá¸áá
áşáᯠ(musicbot áá˛áˇáĄáá°)á
# ááŹáá˝ááş: token áĄáá
áşáááááťááşá¸ stop + disable ááŻááşá
# systemctl stop moviebot && systemctl disable moviebot
# @BotFather á token áĄáá
áşáá°, .tg_env áá˛áááˇáş, ááźáŽá¸ááž re-enableá
áááşáááşá¸á
áŹááťáŹá¸: Token ááąáᏠááááşááááşáááťááşáá°á¸ â Telethon á ááŹáá retry ááŻááşá CPU/RAM ááᯠááťááşááťááşá
áŹá¸á Swap ááážááááş bot á áᯠááŽáááŻáááşáᏠVPS áá
áşááŻááŻáśá¸ freeze ááźá
áşá áááşááąáá˛áˇ service áá˛áááŻááşá crash-loop áááşááąááŹá፠á
áąáŹááˇáşááźááˇáşá
10. OSINT Bot
ááŹáá˛: áĄááźááşáážáŹáá˝áąáá˛áˇ bot: /ip /email /user /auto /inn /all /proxychecká ááźááşáááŻáˇáĄááá â áá
áşá
áąáŹááşáááŻáˇá áá
áşááźáąáá
Proxy áááŻááşáá˝ááş â format + ááťááşáááş fallback
Proxy áá˝áą ááááááąáááşá Bot á proxy.txt (host:port:user:pass áá
áşááźáąáŹááşá¸) áááşá ááťááşáááş áááŻááşáááŻááşáááş (hard-fail áááźá
áş):
import random, requests
def load_proxies(path="/root/info/proxy.txt"):
proxies = []
for line in open(path):
line = line.strip()
if not line or line.startswith("#"):
continue
parts = line.split(":") # host:port:user:pass
if len(parts) == 4:
h, p, u, pw = parts
proxies.append(f"http://{{u}}:{{pw}}@{{h}}:{{p}}")
return proxies
PROXIES = load_proxies()
def fetch(url, timeout=15):
last_err = None
for px in random.sample(PROXIES, min(3, len(PROXIES))):
try:
return requests.get(url, proxies={{"http": px, "https": px}},
timeout=timeout).json()
except Exception as e:
last_err = e # proxy ááą -> ááąáŹááşáá
áşááŻ
# proxy áĄááŻááşááťááş: áááŻááşáááŻááşáááş (ááŹáážááááŹáááş ááąáŹááşá¸)
return requests.get(url, timeout=timeout).json()
Command ááŻáśá
áś
async def cmd_ip(update, context):
target = " ".join(context.args)
if not target:
return await update.message.reply_text("Usage: /ip 1.2.3.4")
try:
d = await asyncio.to_thread(fetch, f"http://ip-api.com/json/{{target}}")
await update.message.reply_text(
f"đ {{d.get('query')}}\n"
f"đ {{d.get('city')}}, {{d.get('country')}}\n"
f"đ˘ {{d.get('isp')}}\n"
f"đ {{d.get('timezone')}}")
except Exception as e:
await update.message.reply_text(f"â Lookup failed: {{e}}")
# /email /user /inn áááşá¸ ááŽááŻáśáᲠâ fetcher áá
áşááŻ, formatter áá
áşááŻá
áŽá
áááşáááşá¸á
áŹááťáŹá¸: áĄáááş crash á proxy format ááááŻááşáᏠ(host:port:user:pass ááᯠcode á áááźáŹá¸ááŹáááşááą) + import ááťáąáŹááş â startup áážáŹ proxy file ááᯠáĄááźá˛ validate ááŻááşá áááş line ááťááşáᲠlog ááŹá¸á
11. VPN Reseller Bot
ááŹáá˛: Reseller ááŽá¸áááˇáş áááşááŹá¸ááąá¸ panel â áááşááŽááąá¸áááş 30% ááťážáąáŹáˇá reseller á áá°áˇ customer áá°áááŻááşáá˝ááşá á
ááşá¸áááşá¸: áá
áşá áĄáááşá¸ááŻáśá¸ 10 accountá áááşááŽáĄáąáŹááşáááąáŹááşá¸á WavePay ááźáᯠ(áááŻáˇ) áĄáááşá
ááşá
áá˝á˛ááŹá¸ â áážááážááŻááşá¸áááá˛áˇ á
ááşá¸ááťááşá¸
# Bot áááŻááşá¸áážáŹ áááŻááşáááŻááş áĄááŹáĄáŹá¸ááŻáśá¸á ááťážááŻáśá¸áᏠoutage áá˛áˇ áĄá
á
# Token: @kbkovpnreseller_bot (BotFather token ááŽá¸áááˇáş)
# Dir: /root/resellerbot (/root/vpnbot áááŻááş)
# Service: resellerbot.service (vpnbot.service áááŻááş)
# DB: /root/resellerbot/reseller.db (SQLite ááŽá¸áááˇáş)
# Admin: áá°áĄáá°áá°á ááŤááąáááˇáş admin/user command áá˝áą ááąáŹáááŹá¸
áááşááŹá¸ááŻááşááąá¸
RESELLER_DISCOUNT = 0.30 # áááşááŽááąá¸áááş 30% ááťážáąáŹáˇ
def wholesale_price(retail_ks: int) -> int:
return int(retail_ks * (1 - RESELLER_DISCOUNT))
async def reseller_buy(update, context):
# Reseller á áááşááŹá¸ááąá¸áá˛áˇáááş; bot á Marzban user áááŻ
# main bot áĄáááŻááşá¸áᲠáá˝ááˇáşá ááŤááąáááˇáş reseller_id áááşááŹá¸
# (report áá˛áˇ áááşáááşá¸áááŻá¸ áá°áážááşááŽááąáŹááş)
uid = update.effective_user.id
if not is_reseller(uid):
return await update.message.reply_text("â Reseller only.")
...
15 áááá
áş health watcher (cron)
# crontab -e:
*/15 * * * * /root/watchers/resellerbot-watch.sh >> /var/log/resellerbot-watch.log 2>&1
#!/bin/bash
# /root/watchers/resellerbot-watch.sh
if ! systemctl is-active --quiet resellerbot; then
MSG="đ¨ Reseller bot ááźáżááŹáážááááş\n\n⢠service áááşááąáááş\ná
á
áşááąá¸ááŤáŚá¸á"
elif [ "$(journalctl -u resellerbot --since '20 minutes ago' -o cat | grep -ci traceback)" -gt 0 ]; then
MSG="đ¨ Reseller bot ááźáżááŹáážááááş\n\n⢠Traceback error áá˝áąáˇáááş\ná
á
áşááąá¸ááŤáŚá¸á"
elif [ "$(journalctl -u resellerbot --since '20 minutes ago' -o cat | grep -c getUpdates)" -eq 0 ]; then
MSG="đ¨ Reseller bot ááźáżááŹáážááááş\n\n⢠Telegram áá˛áˇ áĄáááşááźááşááąáááş\ná
á
áşááąá¸ááŤáŚá¸á"
fi
[ -n "$MSG" ] && send_telegram "$MSG" # ááąáŹááşá¸ááąáááş ááááşááááş
áááşáááşá¸á
áŹááťáŹá¸: Onboarding kit (á
ááşá¸áááşá¸áááş + 30% ááąá¸áááŹá¸ + ááŻááşááąá¸áážáŻá
áŹáááşá¸ + á
ááşá¸ááźáŽá¸ááŹá¸ SpainâUSA ááźáąáŹááşá¸áááşá¸) ááᯠáá
áşááŤáááşá¸ ááźááşááŻáśá¸áááŻáˇá â reseller áĄáá
áşáááŻááşá¸áĄáá˝ááşá Admin, reseller, user ááááşá¸ááťáŻááşáážáŻáá˝áą áááşááąáŹáˇááž ááąáŹáááŹá¸á
12. Health Watchers & Automation
ááŹáá˛: áĄááŹáĄáŹá¸ááŻáśá¸ áĄáááşáážááşáĄáąáŹááş á
áąáŹááˇáşáá˛áˇ áĄáá˝ážáŹ â á ááŹáᎠVPN á
á
áşá 15 áááá
áş service watcherá freeze auto-recoveryá quota watcherá server á ááŻáśá¸ ááąáˇáááŻááşá¸ ááááş rebootá
Watcher ááŻáśá
áś â 2 ááŤáááş fail ááž alert
ááŽá
ááşá¸ááťááşá¸áá
áşááŻá false alarm 99% ááąá áá
áşááŤááťááşáᏠááŹáážáááŻááş; á ááŤáááşááťááşáᏠááááş:
#!/bin/bash
# /root/watchers/vpn-server-health-watch.sh (áĄáááŻááťáŻááş)
STATE=/root/watchers/state/health.json
URL="https://panel.kbko.qzz.io/"
was_down=$(jq -r .was_down "$STATE")
fails=$(jq -r .fails "$STATE")
if curl -sk --max-time 10 "$URL" | grep -q "Marzban"; then
fails=0
if [ "$was_down" = true ]; then
send_telegram "â
Panel ááźááşááąáŹááşá¸áá˝áŹá¸ááŤááźáŽ"
fi
was_down=false
else
fails=$((fails + 1))
if [ "$fails" -ge 2 ] && [ "$was_down" = false ]; then
# ááŻááá áááşáááŻááşááťááş -> ááááş alert, ááźááşáá˝ááˇáşáááŻááşáá˛áˇ
send_telegram "đ¨ Panel down (2 checks)\n\nâ
ááźááşá፠/ âď¸ ááŹá¸áááŻááşáááş"
was_down=true
fi
fi
echo "{{\"was_down\": $was_down, \"fails\": $fails}}" > "$STATE"
# ááąáŹááşá¸ááąáááş ááááşááááşá 2 ááŤáááşáááşááťááşááž áááş alertá
# â
áážáááşááŹá¸áážáᏠááąáŹááş run á ááźááşá
Spain freeze auto-recovery
áĄááááˇáşáĄáááŻááşá¸: "freezeááźá
áşáááŻááşá¸ áááşá¸ááŹáááşá¸á
á
áşááźáŽá¸ áááŻáĄááşáᏠáááŤáᲠááŻááşáááŻááşááŤ"á 15 áááá
áşáááŻááşá¸: SSH ááąáŹ web á
á
áşááŹááąáŹ á ááŤáááş ááťááşáááş IONOS panel áááą Spain ááᯠreboot (áá
áşááźá
áşá
ááşáážáŹ áá
áşááŤáá˛), ááźáŽá¸áááş á
á
áş, ááźááşááŹááᯠreport:
# spain-freeze-auto-recovery áĄááťááşá¸ááťáŻááş (15-áááá
áş cron):
ssh_ok=$(timeout 10 ssh spain "echo ok" 2>/dev/null)
web_ok=$(curl -sk --max-time 10 https://n8n.kbko.qzz.io/ -o /dev/null -w "%{{http_code}}")
if [ -z "$ssh_ok" ] && [ "$web_ok" != "200" ]; then
fails=$((fails + 1))
else
fails=0; rebooted_this_incident=false
fi
if [ "$fails" -ge 2 ] && [ "$rebooted_this_incident" = false ]; then
ionos_panel_restart "31.70.155.75" # áᎠserver áá˛, Restart áá˛
rebooted_this_incident=true
sleep 180; verify_services # dublab, vpnbot, nginx, marzban
send_telegram "đ Spain freeze â reboot ááŻááşááźáŽá¸, service áá˝áąááźááşááąáŹááşá¸ â
"
fi
áĄááźáąáŹááşá¸áááşá¸áĄá
á
áş áá˝áąáˇááźáŽ (Oct 2025) â ááźááşááźáŽá¸ááźáŽ
# áááŻááşááśááźáżáᏠáááŻááş (contract áááˇáş)á áĄááźáąáŹááşá¸áĄá
á
áş:
# - moviebot (BotFather token ááą) + musicbot (login á
áąáŹááˇáş)
# á
áášáááˇáşáĄáááşá¸áááşááźáŹá¸ crash-loop áááş
# - RAM 3.8 GB + swap ááŻáśá¸áááážá -> memory áááĄáŹá¸ááźáąáŹááˇáş áááş
# (panel áážáŹ RUNNING áááŻáˇááź, ááŤááąáááˇáş SSH + web ááą)
#
# áĄááźáą:
systemctl stop moviebot musicbot
systemctl disable moviebot musicbot
fallocate -l 2G /swapfile && chmod 600 /swapfile
mkswap /swapfile && swapon /swapfile
echo "/swapfile none swap sw 0 0" >> /etc/fstab # reboot áááşá¸ áááťááş
ááąáˇáááŻááşá¸ ááááş reboot (07:00)
# 0 7 * * * /root/watchers/daily-restart.sh
# Spain: SSH áá˛áˇ reboot, dublab/resellerbot/vpnbot/nginx/marzban á
á
áş
# USA: SSH áá˛áˇ reboot, marzban container á
á
áş
# Windows: IONOS panel API áá˛áˇ restart, IP + status On á
á
áş
# Report: ááźááşááŹááᯠáĄáááŻááťáŻááş áá
áşá
áąáŹááş, áĄááŻááşá
áááşá¸áááş ááááşááááşá
| Watcher | áĄááťáááş | ááŹááŻááşáᲠ|
| vpn-server-health-watch | á ááŹáᎠ| SSH + panel, throughput â¤áá
áşááąáˇ 2 ááŤ, ááźááşáá˝ááˇáşáááŻááş |
| resellerbot-watch | 15 áááá
áş | Service + traceback + Telegram polling |
| spain-freeze-auto-recovery | 15 áááá
áş | á ááŤáááşááŻáśá¸áááťááş â áĄááťáŹá¸ 1 reboot |
| dublab-gemini-quota-watch | ááąáˇáááŻááşá¸ 09:43 | Gemini key áĄáá
áş áááááąá¸ |
| dublab-reviews-watch | áááá
áş 30 | 5-star review áĄáá
áş áááááąá¸ |
| hourly-db-replica-usa | á ááŹáᎠ| Bot DB á áᯠSpain â USA hot-backup, 24 ááŻááááşá¸ |
áááşáááşá¸á
áŹááťáŹá¸: Alert á admin Telegram ID ááŽá¸áááˇáş â customer áᎠáááşááąáŹáˇááž ááááŻáˇá ááąáŹááşá¸áááŻáážáᏠwatcher áĄáá
áşáááş; áá˝ááˇáşááźáŻááŹá¸áá˛áˇ áĄá
áŻá ááŻáśááąáŹááşááźáŽá
đ ď¸ Project áááŻááşá¸áážáŹ ááŻáśá¸áá˛áˇ ááŻáśá
áśááťáŹá¸
Project áááŻááşá¸áážáŹ ááŻáśá¸áá˛áˇ á
ááşá¸ááťááşá¸ááťáŹá¸ â áááşáááşáá˛áᲠáááşáá˛áˇáááŹ:
- Systemd ááąááŹáááŻááşá¸:
Restart=always, RestartSec=5, enable --nowá Reboot áááşá¸ ááážááşáá˛áˇ service á áááźáŽá¸ááąá¸áá°á¸á
- Secret áá˝áą
.env (chmod 600) áá˛áᲠâ code, chat, log, screenshot áážáŹ áááşááąáŹáˇááž ááááˇáşá Bot token á env áážáŹáá˛á
- áááźááşáááş backup:
cp file{,.bak$(date +%Y%m%d)}á á
áášáááˇáş 30 ááąá¸ááᏠááŹááŽáá˛áˇááťáŽ áááşáá˛áˇááźáŽá
- áĄáá
áşáááş-á
ááşá¸-ááźáŽá¸ááž áĄááąáŹááşá¸ááťááş â user account áá˛áˇ server ááźáąáŹááşá¸áᏠáĄáá°á¸áááá áĄáá
áşáĄááŻááşááźá
áşááž áĄááąáŹááşá¸ááťááşá
- Inline button ááąáŹ
/cmd ááąáŹ â bot command áááŻááşá¸ á ááŻááŻáśá¸á áĄááźá˛á ááźá˝ááşá¸ááťááşááážáá
- ááźááşááŹááᯠáĄáááş â Unicode ááŽá¸áááˇáş (Zawgyi á ááťááş), á
áŹáááŻ, Telegram áá˛áˇ áááŻááşáááşá
- áĄááá˛áˇááŽá¸áááˇáş â project áááŻááşá¸ free tier ááąáŤáşá Paid plan áááşááąáŹáˇááž ááá˝ážááşá¸á
- Live VPN bot ááᯠááááá˛áˇ â áĄááŻááşáá
áşáááŻááşá¸ áááŻááşáááŻááş token, dir, service, DB, resource áááˇáşáááşá
- ááźááşáááşáážáŻáááŻááşáááş ááąá¸ â read-only á
á
áşááąá¸áᏠáá˝ááşáá˝ááşááŻááşáááŻáˇá; ááťááşááŽá¸áááŻááşáᏠáá˝ááˇáşááąáŹááşá¸ (freeze-recovery áĄááááˇáşááá˝á˛áááş)á
- áĄááááşá
áŹáááŻááşá¸ áĄááŹááášááŻááŹáááşá፠â "ááááşáááŻááşááźáŽ" ááťááşá¸ááᯠáᏠááááşáᲠááąá¸ááśááááşá