Spaces:
Runtime error
Runtime error
File size: 7,297 Bytes
5df13c5 7922800 8ec730a 5df13c5 7922800 5df13c5 7922800 5df13c5 7922800 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 |
import os
import io
import random
import logging
import sqlite3
from datetime import datetime
from threading import Thread
import aiohttp
from flask import Flask
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import (
Application, CommandHandler, CallbackQueryHandler, MessageHandler,
ContextTypes, filters
)
# ====== ЛОГИ ======
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
log = logging.getLogger("moti-vpn")
# ====== НАСТРОЙКИ ======
BOT_TOKEN = os.environ.get("botvpntoken")
if not BOT_TOKEN:
raise SystemExit("❌ Не найден ENV botvpntoken")
# База внутри /app/data
DB_PATH = os.path.join(os.path.dirname(__file__), "data", "bot.db")
os.makedirs(os.path.dirname(DB_PATH), exist_ok=True)
PORT = int(os.environ.get("PORT", "7860"))
SUBSCRIPTION_URLS = [
"https://raw.githubusercontent.com/barry-far/V2ray-config/main/Sub1.txt",
"https://raw.githubusercontent.com/barry-far/V2ray-config/main/Sub2.txt",
"https://raw.githubusercontent.com/barry-far/V2ray-config/main/Sub3.txt",
"https://raw.githubusercontent.com/barry-far/V2ray-config/main/Sub4.txt",
"https://raw.githubusercontent.com/barry-far/V2ray-config/main/Sub5.txt",
"https://raw.githubusercontent.com/barry-far/V2ray-config/main/Sub6.txt",
"https://raw.githubusercontent.com/barry-far/V2ray-config/main/Sub7.txt",
"https://raw.githubusercontent.com/barry-far/V2ray-config/main/Sub8.txt",
]
PROTOCOL_URLS = {
"vmess": "https://raw.githubusercontent.com/barry-far/V2ray-config/main/Splitted-By-Protocol/vmess.txt",
"vless": "https://raw.githubusercontent.com/barry-far/V2ray-config/main/Splitted-By-Protocol/vless.txt",
"trojan": "https://raw.githubusercontent.com/barry-far/V2ray-config/main/Splitted-By-Protocol/trojan.txt",
"ss": "https://raw.githubusercontent.com/barry-far/V2ray-config/main/Splitted-By-Protocol/ss.txt",
"ssr": "https://raw.githubusercontent.com/barry-far/V2ray-config/main/Splitted-By-Protocol/ssr.txt",
}
# ====== SQLITE ======
def db():
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row
return conn
def init_db():
with db() as c:
c.execute("""CREATE TABLE IF NOT EXISTS users (
tg_id INTEGER PRIMARY KEY,
username TEXT, first_name TEXT, created_at TEXT, custom_key TEXT
)""")
c.execute("""CREATE TABLE IF NOT EXISTS events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
tg_id INTEGER, type TEXT, payload TEXT, created_at TEXT
)""")
c.commit()
def ensure_user(u):
with db() as c:
cur = c.execute("SELECT tg_id FROM users WHERE tg_id=?", (u.id,))
if not cur.fetchone():
c.execute("INSERT INTO users (tg_id, username, first_name, created_at) VALUES (?,?,?,?)",
(u.id, u.username, u.first_name, datetime.utcnow().isoformat()))
c.commit()
def set_key(tg_id, val):
with db() as c:
c.execute("UPDATE users SET custom_key=? WHERE tg_id=?", (val, tg_id))
c.commit()
def get_key(tg_id):
with db() as c:
cur = c.execute("SELECT custom_key FROM users WHERE tg_id=?", (tg_id,))
row = cur.fetchone()
return row[0] if row else None
# ====== HTTP health ======
flask_app = Flask(__name__)
@flask_app.get("/")
@flask_app.get("/health")
def health():
return {"ok": True}
def run_http():
flask_app.run(host="0.0.0.0", port=PORT, use_reloader=False)
# ====== УТИЛИТЫ ======
async def fetch_text(url):
timeout = aiohttp.ClientTimeout(total=15)
async with aiohttp.ClientSession(timeout=timeout) as s:
async with s.get(url) as r:
r.raise_for_status()
return await r.text()
async def send_file(update: Update, text: str, filename: str):
bio = io.BytesIO(text.encode("utf-8"))
bio.name = filename
await update.effective_message.reply_document(bio, filename=filename)
# ====== МЕНЮ ======
MAIN_MENU = InlineKeyboardMarkup([
[InlineKeyboardButton("🎲 Рандом", callback_data="random"),
InlineKeyboardButton("📚 Подписка", callback_data="subs")],
[InlineKeyboardButton("🧩 Протокол", callback_data="proto")],
[InlineKeyboardButton("🔑 Мой ключ", callback_data="key")]
])
# ====== ХЭНДЛЕРЫ ======
async def start(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
ensure_user(update.effective_user)
await update.message.reply_text("Привет! Я моти впн.", reply_markup=MAIN_MENU)
async def random_sub(update: Update, ctx):
idx = random.randint(0, len(SUBSCRIPTION_URLS)-1)
try:
text = await fetch_text(SUBSCRIPTION_URLS[idx])
await send_file(update, text, f"sub{idx+1}.txt")
except Exception as e:
await update.callback_query.message.reply_text(f"Ошибка: {e}")
async def show_subs(update: Update, ctx):
kb = [[InlineKeyboardButton(f"Sub {i+1}", callback_data=f"sub:{i}")]
for i in range(len(SUBSCRIPTION_URLS))]
await update.callback_query.message.edit_text("Выбери:", reply_markup=InlineKeyboardMarkup(kb))
async def on_sub(update: Update, ctx):
idx = int(update.callback_query.data.split(":")[1])
text = await fetch_text(SUBSCRIPTION_URLS[idx])
await send_file(update, text, f"sub{idx+1}.txt")
async def show_proto(update: Update, ctx):
kb = [[InlineKeyboardButton(k, callback_data=f"proto:{k}")] for k in PROTOCOL_URLS]
await update.callback_query.message.edit_text("Протокол:", reply_markup=InlineKeyboardMarkup(kb))
async def on_proto(update: Update, ctx):
key = update.callback_query.data.split(":")[1]
text = await fetch_text(PROTOCOL_URLS[key])
await send_file(update, text, f"{key}.txt")
async def my_key(update: Update, ctx):
k = get_key(update.effective_user.id)
if k:
await update.callback_query.message.reply_text(f"Твой ключ:\n<code>{k}</code>", parse_mode="HTML")
else:
await update.callback_query.message.reply_text("Нет ключа. Пришли его сюда.")
ctx.user_data["await_key"] = True
async def on_text(update: Update, ctx):
if ctx.user_data.get("await_key"):
val = update.message.text.strip()
set_key(update.effective_user.id, val)
await update.message.reply_text("Сохранил ✅", reply_markup=MAIN_MENU)
ctx.user_data.pop("await_key")
# ====== MAIN ======
if __name__ == "__main__":
init_db()
Thread(target=run_http, daemon=True).start()
app = Application.builder().token(BOT_TOKEN).build()
app.add_handler(CommandHandler("start", start))
app.add_handler(CallbackQueryHandler(random_sub, pattern="^random$"))
app.add_handler(CallbackQueryHandler(show_subs, pattern="^subs$"))
app.add_handler(CallbackQueryHandler(on_sub, pattern="^sub:\\d+$"))
app.add_handler(CallbackQueryHandler(show_proto, pattern="^proto$"))
app.add_handler(CallbackQueryHandler(on_proto, pattern="^proto:"))
app.add_handler(CallbackQueryHandler(my_key, pattern="^key$"))
app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, on_text))
log.info("🚀 Бот запущен...")
app.run_polling()
|