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()