5 Commits

Author SHA1 Message Date
anten-ka
d70e046035 add bootstrap.sh installer 2026-04-09 11:15:43 +03:00
anten-ka
c70cb36a2b GoTelegram v2.3.1: full push
- MTProxy manager with Telegram bot
- 1801 site templates, 18 categories
- Stealth mode (fake-TLS + nginx mask)
- Admin auto-registration in bot
- Fixed frame alignment, bot venv setup
2026-04-09 11:14:41 +03:00
anten-ka
b36fb5cf10 v2.3.1: fix frame alignment + bot venv setup 2026-04-09 11:04:23 +03:00
anten-ka
8b4b4892a4 v2.3.1: bot install with spinner waiting for first admin + auto/manual choice 2026-04-09 10:54:06 +03:00
anten-ka
046a08fdb6 v2.3.1: admin management (auto-register first admin, /addadmin, /deladmin, menu button) 2026-04-09 10:46:50 +03:00
6 changed files with 16963 additions and 16371 deletions

29
.gitignore vendored Normal file
View File

@@ -0,0 +1,29 @@
# Environment
.env
*.env.local
# Python
__pycache__/
*.pyc
venv/
.venv/
# Backups (contain secrets)
backups/
*.tar.gz
*.tar.gz.enc
*.sha256
# Temp
/tmp/
*.tmp
*.swp
# IDE
.vscode/
.idea/
*.code-workspace
# OS
.DS_Store
Thumbs.db

115
bootstrap.sh Executable file
View File

@@ -0,0 +1,115 @@
#!/bin/bash
# GoTelegram Pro — Bootstrap installer for private repo
# Downloads all files and launches install.sh
set -euo pipefail
REPO="anten-ka/gotelegram_pro"
BRANCH="test"
PAT="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"
INSTALL_DIR="/opt/gotelegram"
API="https://api.github.com/repos/${REPO}/contents"
RED='\033[0;31m'
GREEN='\033[0;32m'
CYAN='\033[0;36m'
YELLOW='\033[1;33m'
NC='\033[0m'
BOLD='\033[1m'
echo ""
echo -e " ${YELLOW}╔══════════════════════════════════════════════════════╗${NC}"
echo -e " ${YELLOW}${NC} ${BOLD}GoTelegram Pro — Установка${NC} ${YELLOW}${NC}"
echo -e " ${YELLOW}${NC} ${YELLOW}${NC}"
echo -e " ${YELLOW}${NC} MTProxy менеджер с Telegram-ботом ${YELLOW}${NC}"
echo -e " ${YELLOW}${NC} Stealth-режим, 1800+ шаблонов сайтов ${YELLOW}${NC}"
echo -e " ${YELLOW}╚══════════════════════════════════════════════════════╝${NC}"
echo ""
# Check root
if [ "$(id -u)" -ne 0 ]; then
echo -e " ${RED}${NC} Запустите от root: ${CYAN}sudo bash bootstrap.sh${NC}"
exit 1
fi
# Check dependencies
for cmd in curl jq; do
if ! command -v "$cmd" &>/dev/null; then
echo -e " ${CYAN}${NC} Установка $cmd..."
apt-get update -qq && apt-get install -y -qq "$cmd" >/dev/null 2>&1
fi
done
download_file() {
local remote_path="$1"
local local_path="$2"
local dir
dir=$(dirname "$local_path")
mkdir -p "$dir"
local http_code
http_code=$(curl -sL -w "%{http_code}" -o "$local_path" \
-H "Authorization: token ${PAT}" \
-H "Accept: application/vnd.github.raw" \
"${API}/${remote_path}?ref=${BRANCH}")
if [ "$http_code" != "200" ]; then
echo -e " ${RED}${NC} Ошибка загрузки ${remote_path} (HTTP ${http_code})"
return 1
fi
return 0
}
# File list
FILES=(
"install.sh"
"install_gotelegram_bot.sh"
"templates_catalog.json"
"lib/common.sh"
"lib/telemt.sh"
"lib/telemt_config.sh"
"lib/backup.sh"
"lib/website.sh"
"lib/templates_catalog.sh"
"lib/stats.sh"
"gotelegram-bot/bot.py"
"gotelegram-bot/config.example.env"
"gotelegram-bot/requirements.txt"
"gotelegram-bot/README.md"
)
echo -e " ${CYAN}${NC} Загрузка файлов в ${INSTALL_DIR}..."
mkdir -p "${INSTALL_DIR}/lib" "${INSTALL_DIR}/gotelegram-bot"
failed=0
for f in "${FILES[@]}"; do
if download_file "$f" "${INSTALL_DIR}/${f}"; then
echo -e " ${GREEN}${NC} ${f}"
else
failed=$((failed + 1))
fi
done
if [ "$failed" -gt 0 ]; then
echo ""
echo -e " ${RED}${NC} Не удалось загрузить ${failed} файл(ов)"
echo -e " ${YELLOW}Проверьте токен доступа и подключение к сети${NC}"
exit 1
fi
# Fix permissions and line endings
echo -e " ${CYAN}${NC} Настройка прав..."
chmod +x "${INSTALL_DIR}/install.sh" "${INSTALL_DIR}/install_gotelegram_bot.sh"
chmod +x "${INSTALL_DIR}"/lib/*.sh
sed -i 's/\r$//' "${INSTALL_DIR}/install.sh" "${INSTALL_DIR}/install_gotelegram_bot.sh" "${INSTALL_DIR}"/lib/*.sh 2>/dev/null
# Create symlink
ln -sf "${INSTALL_DIR}/install.sh" /usr/local/bin/gotelegram
echo -e " ${GREEN}${NC} Команда ${CYAN}gotelegram${NC} доступна"
echo ""
echo -e " ${GREEN}${NC} Установка завершена! Запуск..."
echo ""
# Launch
exec bash "${INSTALL_DIR}/install.sh"

View File

@@ -64,15 +64,67 @@ TIP_LINK = "https://pay.cloudtips.ru/p/7410814f"
PROMO_STAMP_FILE = "/opt/gotelegram/.promo_bot_last_shown"
BOT_TOKEN = os.getenv("BOT_TOKEN")
ALLOWED_IDS_STR = os.getenv("ALLOWED_IDS", "")
ENV_FILE = "/opt/gotelegram-bot/.env"
# ── Загрузка ALLOWED_IDS ────────────────────────────────────────────────────
# Поддерживает запятую, пробел, или их комбинацию как разделитель
ALLOWED_IDS: set = set()
for _id_str in ALLOWED_IDS_STR.split(","):
_id_str = _id_str.strip()
if _id_str:
try:
ALLOWED_IDS.add(int(_id_str))
except ValueError:
logging.warning(f"Invalid ALLOWED_IDS entry: {_id_str}")
_WAITING_FOR_ADMIN = False # True если список пуст → ждём первого админа
def _load_allowed_ids() -> None:
"""Загрузить ALLOWED_IDS из переменной окружения."""
global ALLOWED_IDS, _WAITING_FOR_ADMIN
raw = os.getenv("ALLOWED_IDS", "")
ALLOWED_IDS = set()
# Разделители: запятая, пробел, или оба
for part in re.split(r'[,\s]+', raw):
part = part.strip()
if part:
try:
ALLOWED_IDS.add(int(part))
except ValueError:
logging.warning(f"Invalid ALLOWED_IDS entry: {part}")
_WAITING_FOR_ADMIN = len(ALLOWED_IDS) == 0
def _save_allowed_ids() -> None:
"""Сохранить ALLOWED_IDS в .env файл и обновить os.environ."""
global _WAITING_FOR_ADMIN
ids_str = ",".join(str(i) for i in sorted(ALLOWED_IDS))
os.environ["ALLOWED_IDS"] = ids_str
_WAITING_FOR_ADMIN = len(ALLOWED_IDS) == 0
if not os.path.exists(ENV_FILE):
return
try:
with open(ENV_FILE, "r") as f:
lines = f.readlines()
found = False
new_lines = []
for line in lines:
if line.strip().startswith("ALLOWED_IDS="):
if ids_str:
new_lines.append(f"ALLOWED_IDS={ids_str}\n")
# Если пусто — удаляем строку
found = True
else:
new_lines.append(line)
if not found and ids_str:
new_lines.append(f"ALLOWED_IDS={ids_str}\n")
with open(ENV_FILE, "w") as f:
f.writelines(new_lines)
logger.info(f"ALLOWED_IDS updated in .env: {ids_str or '(empty)'}")
except OSError as e:
logger.error(f"Failed to update .env: {e}")
_load_allowed_ids()
LITE_DOMAINS = [
"google.com",
@@ -230,21 +282,41 @@ async def check_old_container() -> Optional[str]:
def is_user_allowed(user_id: int) -> bool:
"""Check if user ID is in ALLOWED_IDS."""
if not ALLOWED_IDS:
return True
"""Check if user ID is in ALLOWED_IDS. If list is empty — waiting for admin."""
if _WAITING_FOR_ADMIN:
return False # Никому не даём доступ пока не назначен админ
return user_id in ALLOWED_IDS
def add_admin(user_id: int) -> None:
"""Добавить администратора и сохранить в .env."""
ALLOWED_IDS.add(user_id)
_save_allowed_ids()
logger.info(f"Admin added: {user_id}")
def remove_admin(user_id: int) -> None:
"""Убрать администратора и сохранить в .env."""
ALLOWED_IDS.discard(user_id)
_save_allowed_ids()
logger.info(f"Admin removed: {user_id}")
async def require_auth(update: Update, context: ContextTypes.DEFAULT_TYPE) -> bool:
"""Check authorization and send error if not allowed."""
if not is_user_allowed(update.effective_user.id):
await update.message.reply_text(
f"Access denied. Your ID: {update.effective_user.id}"
)
logger.warning(
f"Unauthorized access attempt from user {update.effective_user.id}"
)
user_id = update.effective_user.id
# Режим ожидания первого админа — обрабатывается в cmd_start
if _WAITING_FOR_ADMIN:
return False
if not is_user_allowed(user_id):
if update.message:
await update.message.reply_text(
f"⛔ Доступ запрещён.\nВаш ID: <code>{user_id}</code>",
parse_mode="HTML",
)
logger.warning(f"Unauthorized access attempt from user {user_id}")
return False
return True
@@ -286,7 +358,10 @@ def get_main_menu() -> InlineKeyboardMarkup:
InlineKeyboardButton("🗑️ Remove", callback_data="menu_remove"),
],
[
InlineKeyboardButton("👤 Админы", callback_data="menu_admins"),
InlineKeyboardButton(" Credits", callback_data="menu_credits"),
],
[
InlineKeyboardButton("❌ Close", callback_data="close_menu"),
],
]
@@ -299,8 +374,37 @@ def get_main_menu() -> InlineKeyboardMarkup:
async def cmd_start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Start command - show main menu, promo once per day."""
if not await require_auth(update, context):
"""Start command - show main menu, promo once per day.
Если ALLOWED_IDS пуст — режим авто-регистрации первого админа.
"""
user = update.effective_user
user_id = user.id
# ── Режим ожидания первого админа ──
if _WAITING_FOR_ADMIN:
name = user.full_name or user.username or str(user_id)
text = (
f"<b>👋 Привет, {html.escape(name)}!</b>\n\n"
f"Бот ещё не настроен.\n"
f"Ваш Telegram ID: <code>{user_id}</code>\n\n"
f"Назначить вас администратором?"
)
keyboard = InlineKeyboardMarkup([
[
InlineKeyboardButton("✅ Да", callback_data=f"admin_confirm_{user_id}"),
InlineKeyboardButton("❌ Нет", callback_data="admin_cancel"),
]
])
await update.message.reply_text(text, reply_markup=keyboard, parse_mode="HTML")
return
# ── Проверка доступа ──
if not is_user_allowed(user_id):
await update.message.reply_text(
f"⛔ Доступ запрещён.\nВаш ID: <code>{user_id}</code>",
parse_mode="HTML",
)
return
welcome = (
@@ -327,12 +431,14 @@ async def cmd_help(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
return
help_text = (
"<b>GoTelegram Bot Commands</b>\n\n"
"/start - Show main menu\n"
"/help - Show this help message\n"
"/status - Quick status check\n"
"/logs - Show recent logs\n\n"
"Use the inline menu for all other operations."
"<b>GoTelegram Bot — Команды</b>\n\n"
"/start — Главное меню\n"
"/help — Эта справка\n"
"/status — Быстрый статус\n"
"/logs — Последние логи\n"
"/addadmin ID — Добавить админа\n"
"/deladmin ID — Удалить админа\n\n"
"Используйте кнопки меню для остальных операций."
)
await update.message.reply_text(help_text, parse_mode="HTML")
@@ -1322,6 +1428,114 @@ async def cb_ssl_status(update: Update, context: ContextTypes.DEFAULT_TYPE) -> N
await safe_edit_message(query,text, reply_markup=keyboard, parse_mode="HTML")
# ============================================================================
# ADMIN MANAGEMENT
# ============================================================================
async def cb_menu_admins(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Показать список админов и кнопки управления."""
query = update.callback_query
await query.answer()
if ALLOWED_IDS:
ids_list = "\n".join(f" • <code>{uid}</code>" for uid in sorted(ALLOWED_IDS))
text = f"<b>👤 Администраторы</b>\n\n{ids_list}\n"
else:
text = "<b>👤 Администраторы</b>\n\n<i>Список пуст — доступ для всех</i>\n"
text += (
f"\nВсего: {len(ALLOWED_IDS)}\n\n"
"Чтобы <b>добавить</b> — перешлите любое сообщение от нового админа, "
"или отправьте команду:\n"
"<code>/addadmin 123456789</code>\n\n"
"Чтобы <b>удалить</b>:\n"
"<code>/deladmin 123456789</code>"
)
keyboard = InlineKeyboardMarkup([
[InlineKeyboardButton("« Назад", callback_data="menu_main")],
])
await safe_edit_message(query, text, reply_markup=keyboard, parse_mode="HTML")
async def cmd_addadmin(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""/addadmin ID [ID2 ID3 ...] — добавить админа вручную."""
if not is_user_allowed(update.effective_user.id):
await update.message.reply_text(
f"⛔ Доступ запрещён.\nВаш ID: <code>{update.effective_user.id}</code>",
parse_mode="HTML",
)
return
args = context.args or []
if not args:
await update.message.reply_text(
"Использование: <code>/addadmin ID [ID2 ID3 ...]</code>\n"
"Пример: <code>/addadmin 123456789 987654321</code>",
parse_mode="HTML",
)
return
added = []
errors = []
for a in args:
a = a.strip().replace(",", "")
if not a:
continue
try:
uid = int(a)
add_admin(uid)
added.append(str(uid))
except ValueError:
errors.append(a)
parts = []
if added:
parts.append(f"✅ Добавлены: {', '.join(added)}")
if errors:
parts.append(f"❌ Ошибки: {', '.join(errors)}")
await update.message.reply_text("\n".join(parts), parse_mode="HTML")
async def cmd_deladmin(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""/deladmin ID — удалить админа."""
if not is_user_allowed(update.effective_user.id):
await update.message.reply_text(
f"⛔ Доступ запрещён.\nВаш ID: <code>{update.effective_user.id}</code>",
parse_mode="HTML",
)
return
args = context.args or []
if not args:
await update.message.reply_text(
"Использование: <code>/deladmin ID</code>",
parse_mode="HTML",
)
return
removed = []
for a in args:
a = a.strip().replace(",", "")
try:
uid = int(a)
if uid == update.effective_user.id:
await update.message.reply_text("⚠️ Нельзя удалить себя!")
continue
if uid in ALLOWED_IDS:
remove_admin(uid)
removed.append(str(uid))
else:
await update.message.reply_text(f"ID {uid} не найден в списке")
except ValueError:
await update.message.reply_text(f"❌ Некорректный ID: {html.escape(a)}")
if removed:
await update.message.reply_text(f"✅ Удалены: {', '.join(removed)}")
# ============================================================================
# PROMO & CREDITS
# ============================================================================
@@ -1463,9 +1677,43 @@ async def handle_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
query = update.callback_query
data = query.data
# ── Авто-регистрация админа (до проверки доступа!) ──
if data.startswith("admin_confirm_"):
await query.answer()
try:
new_admin_id = int(data.split("_")[-1])
except (ValueError, IndexError):
await safe_edit_message(query, "❌ Ошибка: некорректный ID")
return
# Безопасность: только тот кто нажал кнопку может стать админом
if update.effective_user.id != new_admin_id:
await query.answer("Эта кнопка не для вас", show_alert=True)
return
# Race condition: если кто-то уже стал админом
if not _WAITING_FOR_ADMIN:
await safe_edit_message(query, " Администратор уже назначен.")
return
add_admin(new_admin_id)
await safe_edit_message(
query,
f"✅ <b>Вы назначены администратором!</b>\n\n"
f"ID: <code>{new_admin_id}</code>\n\n"
f"Нажмите /start чтобы открыть меню.",
parse_mode="HTML",
)
return
if data == "admin_cancel":
await query.answer()
await safe_edit_message(
query,
"👋 Ок. Напишите /start когда будете готовы.",
)
return
# Access control
if not is_user_allowed(update.effective_user.id):
await query.answer("Access denied")
await query.answer("Доступ запрещён")
return
# Main menu
@@ -1500,6 +1748,7 @@ async def handle_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
"menu_website": cb_menu_website,
"menu_promo": cb_menu_promo,
"menu_credits": cb_menu_credits,
"menu_admins": cb_menu_admins,
"menu_remove": cb_menu_remove,
"install_mode_lite": cb_install_mode_lite,
"install_mode_pro": cb_install_mode_pro,
@@ -1560,6 +1809,8 @@ def main() -> None:
application.add_handler(CommandHandler("help", cmd_help))
application.add_handler(CommandHandler("status", cmd_status))
application.add_handler(CommandHandler("logs", cmd_logs))
application.add_handler(CommandHandler("addadmin", cmd_addadmin))
application.add_handler(CommandHandler("deladmin", cmd_deladmin))
# Callback query handler (buttons)
application.add_handler(CallbackQueryHandler(handle_callback))

View File

@@ -799,14 +799,25 @@ bot_install() {
[ -z "$token" ] && log_error "Токен не может быть пустым"
done
echo -ne " ${WHITE}ID администратора (Enter = доступ для всех):${NC} "
read -r admin_id
echo ""
echo -e " ${WHITE}Как добавить администратора?${NC}"
echo -e " ${CYAN}1${NC}) Автоматически — бот определит ID при первом /start"
echo -e " ${CYAN}2${NC}) Вручную — ввести ID сейчас"
echo -ne " ${WHITE}Выбор [1]:${NC} "
read -r admin_mode
admin_mode="${admin_mode:-1}"
local admin_ids=""
if [ "$admin_mode" = "2" ]; then
echo -ne " ${WHITE}ID администраторов (через пробел/запятую):${NC} "
read -r admin_ids
admin_ids=$(echo "$admin_ids" | tr ' ' ',' | sed 's/,,*/,/g; s/^,//; s/,$//')
fi
{
echo "BOT_TOKEN=$token"
[ -n "$admin_id" ] && echo "ALLOWED_IDS=$admin_id"
[ -n "$admin_ids" ] && echo "ALLOWED_IDS=$admin_ids"
} > "$BOT_DIR/.env"
chmod 600 "$BOT_DIR/.env"
log_success ".env создан"
else
@@ -835,6 +846,59 @@ SVCEOF
systemctl enable "$BOT_SERVICE" &>/dev/null
systemctl restart "$BOT_SERVICE" 2>/dev/null || systemctl start "$BOT_SERVICE"
# Если авто-режим — ждём пока бот словит первого админа
local has_ids
has_ids=$(grep "^ALLOWED_IDS=" "$BOT_DIR/.env" 2>/dev/null | cut -d= -f2)
if [ -z "$has_ids" ]; then
echo ""
echo -e " ${YELLOW}╔══════════════════════════════════════════════════════╗${NC}"
echo -e " ${YELLOW}${NC} ${BOLD}Ожидание администратора${NC} ${YELLOW}${NC}"
echo -e " ${YELLOW}${NC} ${YELLOW}${NC}"
echo -e " ${YELLOW}${NC} Откройте бота в Telegram и отправьте ${CYAN}/start${NC} ${YELLOW}${NC}"
echo -e " ${YELLOW}${NC} Бот автоматически назначит вас администратором ${YELLOW}${NC}"
echo -e " ${YELLOW}${NC} ${YELLOW}${NC}"
echo -e " ${YELLOW}${NC} ${DIM}Нажмите Ctrl+C чтобы пропустить${NC} ${YELLOW}${NC}"
echo -e " ${YELLOW}╚══════════════════════════════════════════════════════╝${NC}"
echo ""
local frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏')
local i=0
local waited=0
local max_wait=300 # 5 минут максимум
# Ловим Ctrl+C чтобы выйти из ожидания без убийства скрипта
local interrupted=0
trap 'interrupted=1' INT
while [ $waited -lt $max_wait ] && [ $interrupted -eq 0 ]; do
printf "\r ${CYAN}${frames[$i]}${NC} Ожидание... напишите /start боту (%d сек) " "$waited" >&2
i=$(( (i+1) % ${#frames[@]} ))
sleep 1
waited=$((waited + 1))
# Проверяем появился ли ALLOWED_IDS
has_ids=$(grep "^ALLOWED_IDS=" "$BOT_DIR/.env" 2>/dev/null | cut -d= -f2)
if [ -n "$has_ids" ]; then
break
fi
done
trap - INT
printf "\r\033[K" >&2 # очистить строку со спиннером
if [ -n "$has_ids" ]; then
echo ""
log_success "Администратор назначен!"
echo -e " ${WHITE}ID:${NC} ${GREEN}${has_ids}${NC}"
elif [ $interrupted -eq 1 ]; then
echo ""
log_warning "Пропущено. Добавить админа позже: меню → Telegram-бот → Настройки"
else
echo ""
log_warning "Таймаут (5 мин). Добавить админа: меню → Telegram-бот → Настройки"
fi
fi
echo ""
log_success "Бот установлен и запущен!"
echo -e " ${DIM}Проверка: systemctl status $BOT_SERVICE${NC}"
@@ -908,9 +972,10 @@ bot_edit_config() {
fi
;;
2)
echo -ne " ${WHITE}ALLOWED_IDS (через запятую, пусто = все):${NC} "
echo -ne " ${WHITE}ALLOWED_IDS (через пробел/запятую, пусто = авто):${NC} "
read -r new_ids
new_ids=$(echo "$new_ids" | tr -d '[:space:]')
# Нормализуем: пробелы и запятые → запятые, убираем лишнее
new_ids=$(echo "$new_ids" | tr ' ' ',' | sed 's/,,*/,/g; s/^,//; s/,$//')
if grep -q "^ALLOWED_IDS=" "$BOT_DIR/.env" 2>/dev/null; then
if [ -n "$new_ids" ]; then
sed -i "s|^ALLOWED_IDS=.*|ALLOWED_IDS=$new_ids|" "$BOT_DIR/.env"

132
install_gotelegram_bot.sh Executable file
View File

@@ -0,0 +1,132 @@
#!/bin/bash
# GoTelegram v2.2.1 — Установка Telegram-бота
# Создаёт venv, ставит зависимости, настраивает systemd
set -e
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
BOT_DIR="/opt/gotelegram-bot"
SERVICE_NAME="gotelegram-bot"
GOTELEGRAM_DIR="/opt/gotelegram"
if [ "$EUID" -ne 0 ]; then
echo -e "${RED}Запустите с sudo.${NC}"
exit 1
fi
echo -e "${CYAN}╔═══════════════════════════════════════════╗${NC}"
echo -e "${CYAN}${NC} ${GREEN}GoTelegram v2.2.1 — Установка бота${NC} ${CYAN}${NC}"
echo -e "${CYAN}╚═══════════════════════════════════════════╝${NC}"
echo ""
# ── Python ───────────────────────────────────────────────────────────────────
if ! command -v python3 &>/dev/null; then
echo -e "${YELLOW}[*] Установка python3...${NC}"
if command -v apt-get &>/dev/null; then
apt-get update -qq && apt-get install -y -qq python3 python3-pip python3-venv
elif command -v dnf &>/dev/null; then
dnf install -y -q python3 python3-pip
elif command -v yum &>/dev/null; then
yum install -y -q python3 python3-pip
fi
fi
# ── Каталог бота ─────────────────────────────────────────────────────────────
mkdir -p "$BOT_DIR"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if [ -f "$SCRIPT_DIR/gotelegram-bot/bot.py" ]; then
echo -e "${GREEN}[*] Копирование файлов бота...${NC}"
cp "$SCRIPT_DIR/gotelegram-bot/bot.py" "$BOT_DIR/"
cp "$SCRIPT_DIR/gotelegram-bot/requirements.txt" "$BOT_DIR/"
[ -f "$SCRIPT_DIR/gotelegram-bot/config.example.env" ] && cp "$SCRIPT_DIR/gotelegram-bot/config.example.env" "$BOT_DIR/"
else
echo -e "${RED}Файлы бота не найдены в $SCRIPT_DIR/gotelegram-bot/${NC}"
exit 1
fi
# Копируем каталог шаблонов
if [ -f "$SCRIPT_DIR/templates_catalog.json" ]; then
mkdir -p "$GOTELEGRAM_DIR"
cp "$SCRIPT_DIR/templates_catalog.json" "$GOTELEGRAM_DIR/"
echo -e "${GREEN}[*] Каталог шаблонов скопирован${NC}"
fi
# ── Virtual environment ──────────────────────────────────────────────────────
if [ ! -d "$BOT_DIR/venv" ]; then
echo -e "${GREEN}[*] Создание виртуального окружения...${NC}"
python3 -m venv "$BOT_DIR/venv"
fi
echo -e "${GREEN}[*] Установка зависимостей...${NC}"
"$BOT_DIR/venv/bin/pip" install -r "$BOT_DIR/requirements.txt" -q
# ── Конфигурация ─────────────────────────────────────────────────────────────
if [ ! -f "$BOT_DIR/.env" ]; then
echo ""
echo -e "${YELLOW}Введите BOT_TOKEN от @BotFather:${NC}"
TOKEN=""
while [ -z "$TOKEN" ]; do
read -r TOKEN
TOKEN=$(echo "$TOKEN" | tr -d '[:space:]')
[ -z "$TOKEN" ] && echo -e "${RED}Токен не может быть пустым.${NC}"
done
echo -ne "${YELLOW}ID администратора (Enter = доступ для всех):${NC} "
read -r ADMIN_ID
{
echo "BOT_TOKEN=$TOKEN"
[ -n "$ADMIN_ID" ] && echo "ALLOWED_IDS=$ADMIN_ID"
} > "$BOT_DIR/.env"
chmod 600 "$BOT_DIR/.env"
echo -e "${GREEN}[*] .env создан${NC}"
else
echo -e "${GREEN}[*] .env уже существует${NC}"
fi
# ── Systemd ──────────────────────────────────────────────────────────────────
cat > "/etc/systemd/system/${SERVICE_NAME}.service" << EOF
[Unit]
Description=GoTelegram v2.2.1 Telegram Bot
After=network.target
[Service]
Type=simple
WorkingDirectory=$BOT_DIR
ExecStart=$BOT_DIR/venv/bin/python $BOT_DIR/bot.py
Restart=always
RestartSec=5
Environment=PATH=$BOT_DIR/venv/bin:/usr/bin
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable "$SERVICE_NAME"
systemctl restart "$SERVICE_NAME" 2>/dev/null || systemctl start "$SERVICE_NAME"
echo ""
echo -e "${GREEN}╔═══════════════════════════════════════════╗${NC}"
echo -e "${GREEN}║ ✅ Бот установлен и запущен! ║${NC}"
echo -e "${GREEN}╚═══════════════════════════════════════════╝${NC}"
echo ""
echo -e "Проверка: ${CYAN}systemctl status $SERVICE_NAME${NC}"
echo -e "Логи: ${CYAN}journalctl -u $SERVICE_NAME -f${NC}"
echo -e "Настройки: ${CYAN}$BOT_DIR/.env${NC}"
echo ""
# Благодарности
echo -e "${CYAN}─────────────────────────────────────────────${NC}"
echo -e "💜 Спасибо авторам открытых проектов:"
echo -e " ${CYAN}telemt${NC} — MTProxy engine (Rust)"
echo -e " ${CYAN}HTML5 UP${NC} — шаблоны сайтов (CC BY 3.0)"
echo -e " ${CYAN}learning-zone${NC} — 150+ HTML5 шаблонов"
echo -e " ${CYAN}Start Bootstrap${NC} — Bootstrap шаблоны (MIT)"
echo -e "${CYAN}─────────────────────────────────────────────${NC}"

File diff suppressed because it is too large Load Diff