motidocktest / app.py
bafifi4972's picture
Update app.py
7922800 verified
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()