- Portfolio CRUD: добавление, редактирование, удаление, переключение публикации - Services CRUD: полное управление услугами с возможностью активации/деактивации - Banner system: новая модель Banner с CRUD операциями и аналитикой кликов - Telegram integration: расширенные настройки бота, обнаружение чатов, отправка сообщений - Media management: улучшенная загрузка файлов с оптимизацией изображений и превью - UI improvements: обновлённые админ-панели с rich-text редактором и drag&drop загрузкой - Database: добавлена таблица banners с полями для баннеров и аналитики
885 lines
47 KiB
Plaintext
885 lines
47 KiB
Plaintext
<!DOCTYPE html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Telegram Bot - SmartSolTech Admin</title>
|
||
|
||
<!-- Tailwind CSS -->
|
||
<script src="https://cdn.tailwindcss.com"></script>
|
||
|
||
<!-- Font Awesome -->
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||
|
||
<!-- Custom CSS -->
|
||
<link rel="stylesheet" href="/css/main.css">
|
||
<link rel="stylesheet" href="/css/fixes.css">
|
||
</head>
|
||
<body class="bg-gray-100">
|
||
<!-- Admin Header -->
|
||
<header class="bg-white shadow-sm border-b">
|
||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||
<div class="flex justify-between items-center h-16">
|
||
<div class="flex items-center">
|
||
<h1 class="text-xl font-semibold text-gray-900">
|
||
<i class="fas fa-cogs mr-2"></i>
|
||
SmartSolTech Admin
|
||
</h1>
|
||
</div>
|
||
<div class="flex items-center space-x-4">
|
||
<span class="text-sm text-gray-600">
|
||
Добро пожаловать, <%= user ? user.name : 'Admin' %>!
|
||
</span>
|
||
<a href="/" class="text-gray-500 hover:text-gray-700">
|
||
<i class="fas fa-external-link-alt mr-1"></i>
|
||
Посмотреть сайт
|
||
</a>
|
||
<form action="/admin/logout" method="post" class="inline">
|
||
<button type="submit" class="text-red-600 hover:text-red-800">
|
||
<i class="fas fa-sign-out-alt mr-1"></i>
|
||
Выход
|
||
</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<div class="flex">
|
||
<!-- Admin Sidebar -->
|
||
<aside class="w-64 bg-white shadow-sm admin-sidebar min-h-screen">
|
||
<nav class="mt-5 px-2">
|
||
<div class="space-y-1">
|
||
<a href="/admin/dashboard" class="group flex items-center px-2 py-2 text-sm font-medium rounded-md text-gray-600 hover:bg-gray-50 hover:text-gray-900">
|
||
<i class="fas fa-tachometer-alt mr-3"></i>
|
||
Панель управления
|
||
</a>
|
||
<a href="/admin/portfolio" class="group flex items-center px-2 py-2 text-sm font-medium rounded-md text-gray-600 hover:bg-gray-50 hover:text-gray-900">
|
||
<i class="fas fa-briefcase mr-3"></i>
|
||
Портфолио
|
||
</a>
|
||
<a href="/admin/services" class="group flex items-center px-2 py-2 text-sm font-medium rounded-md text-gray-600 hover:bg-gray-50 hover:text-gray-900">
|
||
<i class="fas fa-cog mr-3"></i>
|
||
Услуги
|
||
</a>
|
||
<a href="/admin/contacts" class="group flex items-center px-2 py-2 text-sm font-medium rounded-md text-gray-600 hover:bg-gray-50 hover:text-gray-900">
|
||
<i class="fas fa-envelope mr-3"></i>
|
||
Сообщения
|
||
</a>
|
||
<a href="/admin/media" class="group flex items-center px-2 py-2 text-sm font-medium rounded-md text-gray-600 hover:bg-gray-50 hover:text-gray-900">
|
||
<i class="fas fa-images mr-3"></i>
|
||
Медиа
|
||
</a>
|
||
<a href="/admin/settings" class="group flex items-center px-2 py-2 text-sm font-medium rounded-md text-gray-600 hover:bg-gray-50 hover:text-gray-900">
|
||
<i class="fas fa-cogs mr-3"></i>
|
||
Настройки
|
||
</a>
|
||
<a href="/admin/telegram" class="group flex items-center px-2 py-2 text-sm font-medium rounded-md bg-blue-100 text-blue-700">
|
||
<i class="fab fa-telegram mr-3"></i>
|
||
Telegram Bot
|
||
</a>
|
||
<a href="/admin/banner-editor" class="group flex items-center px-2 py-2 text-sm font-medium rounded-md text-gray-600 hover:bg-gray-50 hover:text-gray-900">
|
||
<i class="fas fa-paint-brush mr-3"></i>
|
||
Редактор баннеров
|
||
</a>
|
||
</div>
|
||
</nav>
|
||
</aside>
|
||
|
||
<!-- Main Content -->
|
||
<main class="flex-1 p-8">
|
||
<div class="space-y-6">
|
||
<!-- Header -->
|
||
<div class="bg-white shadow rounded-lg p-6">
|
||
<div class="flex items-center justify-between">
|
||
<div>
|
||
<h1 class="text-2xl font-bold text-gray-900 flex items-center">
|
||
<i class="fab fa-telegram mr-3 text-blue-500"></i>
|
||
Telegram Bot
|
||
</h1>
|
||
<p class="mt-2 text-gray-600">Настройка и управление уведомлениями через Telegram</p>
|
||
</div>
|
||
<div class="flex items-center space-x-3">
|
||
<div class="flex items-center">
|
||
<div class="w-3 h-3 rounded-full <%= botConfigured ? 'bg-green-500' : 'bg-red-500' %> mr-2"></div>
|
||
<span class="text-sm font-medium <%= botConfigured ? 'text-green-700' : 'text-red-700' %>">
|
||
<%= botConfigured ? 'Подключен' : 'Не настроен' %>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Bot Configuration -->
|
||
<div class="bg-white shadow rounded-lg p-6">
|
||
<h3 class="text-lg font-semibold text-gray-900 mb-6">
|
||
<i class="fas fa-cog mr-2 text-blue-500"></i>
|
||
Конфигурация бота
|
||
</h3>
|
||
|
||
<form id="bot-config-form" class="space-y-6">
|
||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||
<!-- Bot Token -->
|
||
<div>
|
||
<label for="botToken" class="block text-sm font-medium text-gray-700 mb-2">
|
||
Токен бота *
|
||
</label>
|
||
<div class="relative">
|
||
<input type="password" id="botToken" name="botToken"
|
||
value="<%= botToken %>"
|
||
placeholder="1234567890:ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||
class="block w-full pr-10 border border-gray-300 rounded-lg px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
||
<button type="button" onclick="toggleTokenVisibility()"
|
||
class="absolute inset-y-0 right-0 pr-3 flex items-center">
|
||
<i id="token-eye" class="fas fa-eye text-gray-400"></i>
|
||
</button>
|
||
</div>
|
||
<p class="mt-1 text-sm text-gray-500">
|
||
Получите токен от <a href="https://t.me/BotFather" target="_blank" class="text-blue-600 underline">@BotFather</a>
|
||
</p>
|
||
</div>
|
||
|
||
<!-- Default Chat ID -->
|
||
<div>
|
||
<label for="chatId" class="block text-sm font-medium text-gray-700 mb-2">
|
||
ID чата по умолчанию
|
||
</label>
|
||
<input type="text" id="chatId" name="chatId"
|
||
value="<%= chatId %>"
|
||
placeholder="-1001234567890"
|
||
class="block w-full border border-gray-300 rounded-lg px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
||
<p class="mt-1 text-sm text-gray-500">
|
||
Оставьте пустым, если будете выбирать чат из списка
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="flex space-x-3">
|
||
<button type="submit" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg transition-colors">
|
||
<i class="fas fa-save mr-2"></i>
|
||
Сохранить настройки
|
||
</button>
|
||
<button type="button" onclick="getBotInfo()" class="bg-gray-600 hover:bg-gray-700 text-white px-4 py-2 rounded-lg transition-colors">
|
||
<i class="fas fa-info-circle mr-2"></i>
|
||
Получить информацию о боте
|
||
</button>
|
||
</div>
|
||
</form>
|
||
|
||
<div id="config-result" class="mt-4 hidden"></div>
|
||
</div>
|
||
|
||
<% if (botConfigured) { %>
|
||
<!-- Bot Information -->
|
||
<div class="bg-white shadow rounded-lg p-6">
|
||
<div class="flex items-center justify-between mb-6">
|
||
<h3 class="text-lg font-semibold text-gray-900">
|
||
<i class="fas fa-robot mr-2 text-green-500"></i>
|
||
Информация о боте
|
||
</h3>
|
||
<button onclick="refreshBotInfo()" class="text-blue-600 hover:text-blue-800 text-sm">
|
||
<i class="fas fa-refresh mr-1"></i>
|
||
Обновить
|
||
</button>
|
||
</div>
|
||
|
||
<div id="bot-info-container">
|
||
<% if (botInfo) { %>
|
||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||
<div class="bg-gray-50 p-4 rounded-lg">
|
||
<div class="text-sm text-gray-600">Имя бота</div>
|
||
<div class="text-lg font-semibold text-gray-900">@<%= botInfo.username %></div>
|
||
</div>
|
||
<div class="bg-gray-50 p-4 rounded-lg">
|
||
<div class="text-sm text-gray-600">Отображаемое имя</div>
|
||
<div class="text-lg font-semibold text-gray-900"><%= botInfo.first_name %></div>
|
||
</div>
|
||
<div class="bg-gray-50 p-4 rounded-lg">
|
||
<div class="text-sm text-gray-600">ID бота</div>
|
||
<div class="text-lg font-semibold text-gray-900"><%= botInfo.id %></div>
|
||
</div>
|
||
<div class="bg-gray-50 p-4 rounded-lg">
|
||
<div class="text-sm text-gray-600">Может читать сообщения</div>
|
||
<div class="text-lg font-semibold <%= botInfo.can_read_all_group_messages ? 'text-green-600' : 'text-red-600' %>">
|
||
<%= botInfo.can_read_all_group_messages ? 'Да' : 'Нет' %>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<% } else { %>
|
||
<div class="text-center py-4 text-gray-500">
|
||
<i class="fas fa-robot text-4xl text-gray-300 mb-2"></i>
|
||
<p>Настройте токен бота для получения информации</p>
|
||
</div>
|
||
<% } %>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Available Chats -->
|
||
<div class="bg-white shadow rounded-lg p-6">
|
||
<div class="flex items-center justify-between mb-6">
|
||
<h3 class="text-lg font-semibold text-gray-900">
|
||
<i class="fas fa-comments mr-2 text-blue-500"></i>
|
||
Доступные чаты
|
||
</h3>
|
||
<button onclick="discoverChats()" class="bg-blue-600 hover:bg-blue-700 text-white px-3 py-1 rounded text-sm transition-colors">
|
||
<i class="fas fa-search mr-1"></i>
|
||
Найти чаты
|
||
</button>
|
||
</div>
|
||
|
||
<div id="available-chats-container">
|
||
<% if (availableChats && availableChats.length > 0) { %>
|
||
<div class="grid gap-3">
|
||
<% availableChats.forEach(chat => { %>
|
||
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
|
||
<div class="flex items-center space-x-3">
|
||
<div class="w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center">
|
||
<i class="fas <%= chat.type === 'group' || chat.type === 'supergroup' ? 'fa-users' : 'fa-user' %> text-blue-600 text-sm"></i>
|
||
</div>
|
||
<div>
|
||
<div class="font-medium text-gray-900"><%= chat.title %></div>
|
||
<div class="text-sm text-gray-500">
|
||
<%= chat.type %> • ID: <%= chat.id %>
|
||
<% if (chat.username) { %>• @<%= chat.username %><% } %>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<button onclick="selectChat('<%= chat.id %>', '<%= chat.title %>')"
|
||
class="text-blue-600 hover:text-blue-800 text-sm">
|
||
Выбрать
|
||
</button>
|
||
</div>
|
||
<% }); %>
|
||
</div>
|
||
<% } else { %>
|
||
<div class="text-center py-8 text-gray-500">
|
||
<i class="fas fa-comments text-4xl text-gray-300 mb-4"></i>
|
||
<p class="mb-2">Чаты не найдены</p>
|
||
<p class="text-sm">Отправьте боту сообщение или добавьте его в группу, затем нажмите "Найти чаты"</p>
|
||
</div>
|
||
<% } %>
|
||
</div>
|
||
</div>
|
||
<% } %>
|
||
|
||
<!-- Message Sender -->
|
||
<div class="bg-white shadow rounded-lg p-6">
|
||
<h3 class="text-lg font-semibold text-gray-900 mb-6">
|
||
<i class="fas fa-paper-plane mr-2 text-purple-500"></i>
|
||
Отправить сообщение
|
||
</h3>
|
||
|
||
<form id="send-message-form" class="space-y-6">
|
||
<!-- Message Content -->
|
||
<div>
|
||
<label for="messageText" class="block text-sm font-medium text-gray-700 mb-2">
|
||
Текст сообщения *
|
||
</label>
|
||
<textarea id="messageText" name="message" rows="4" required
|
||
placeholder="Введите сообщение..."
|
||
class="block w-full border border-gray-300 rounded-lg px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:border-blue-500"></textarea>
|
||
</div>
|
||
|
||
<!-- Recipients -->
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-2">
|
||
Получатели
|
||
</label>
|
||
<div id="recipients-container" class="space-y-2">
|
||
<% if (availableChats && availableChats.length > 0) { %>
|
||
<% availableChats.forEach(chat => { %>
|
||
<label class="flex items-center">
|
||
<input type="checkbox" name="chatIds" value="<%= chat.id %>"
|
||
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded">
|
||
<span class="ml-2 text-sm text-gray-900">
|
||
<%= chat.title %>
|
||
<span class="text-gray-500">(<%= chat.type %>)</span>
|
||
</span>
|
||
</label>
|
||
<% }); %>
|
||
<% } else { %>
|
||
<div class="text-sm text-gray-500">
|
||
<i class="fas fa-info-circle mr-1"></i>
|
||
Сообщение будет отправлено в чат по умолчанию
|
||
</div>
|
||
<% } %>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Message Options -->
|
||
<div class="bg-gray-50 p-4 rounded-lg">
|
||
<h4 class="text-sm font-medium text-gray-900 mb-3">Настройки сообщения</h4>
|
||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||
<label class="flex items-center">
|
||
<input type="checkbox" name="disableWebPagePreview"
|
||
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded">
|
||
<span class="ml-2 text-sm text-gray-900">Отключить предпросмотр ссылок</span>
|
||
</label>
|
||
<label class="flex items-center">
|
||
<input type="checkbox" name="disableNotification"
|
||
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded">
|
||
<span class="ml-2 text-sm text-gray-900">Тихое уведомление</span>
|
||
</label>
|
||
</div>
|
||
<div class="mt-4">
|
||
<label for="parseMode" class="block text-sm font-medium text-gray-700 mb-1">
|
||
Формат текста
|
||
</label>
|
||
<select name="parseMode" id="parseMode"
|
||
class="block w-full border border-gray-300 rounded-md px-3 py-2 text-sm">
|
||
<option value="HTML">HTML</option>
|
||
<option value="Markdown">Markdown</option>
|
||
<option value="">Обычный текст</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Submit Buttons -->
|
||
<div class="flex justify-between">
|
||
<button type="button" onclick="previewMessage()"
|
||
class="inline-flex items-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50">
|
||
<i class="fas fa-eye mr-2"></i>
|
||
Предпросмотр
|
||
</button>
|
||
<div class="space-x-3">
|
||
<button type="button" onclick="testConnection()"
|
||
class="inline-flex items-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50">
|
||
<i class="fas fa-vial mr-2"></i>
|
||
Тест соединения
|
||
</button>
|
||
<button type="submit" id="sendMessageBtn"
|
||
class="inline-flex items-center px-6 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700">
|
||
<i class="fas fa-paper-plane mr-2"></i>
|
||
Отправить сообщение
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
|
||
<div id="send-result" class="mt-4 hidden"></div>
|
||
</div>
|
||
<!-- Bot Status and Controls -->
|
||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||
<!-- Connection Test -->
|
||
<div class="bg-white shadow rounded-lg p-6">
|
||
<h3 class="text-lg font-semibold text-gray-900 mb-4">
|
||
<i class="fas fa-plug mr-2 text-green-500"></i>
|
||
Проверка подключения
|
||
</h3>
|
||
<p class="text-gray-600 mb-4">
|
||
Отправить тестовое сообщение для проверки работоспособности бота.
|
||
</p>
|
||
<button id="test-connection" class="w-full bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded-lg transition-colors">
|
||
<i class="fas fa-vial mr-2"></i>
|
||
Тестировать подключение
|
||
</button>
|
||
<div id="test-result" class="mt-4 hidden"></div>
|
||
</div>
|
||
|
||
<!-- Send Custom Message -->
|
||
<div class="bg-white shadow rounded-lg p-6">
|
||
<h3 class="text-lg font-semibold text-gray-900 mb-4">
|
||
<i class="fas fa-paper-plane mr-2 text-blue-500"></i>
|
||
Отправить сообщение
|
||
</h3>
|
||
<form id="send-message-form">
|
||
<div class="mb-4">
|
||
<label class="block text-sm font-medium text-gray-700 mb-2">
|
||
Сообщение
|
||
</label>
|
||
<textarea
|
||
id="custom-message"
|
||
rows="4"
|
||
class="w-full border border-gray-300 rounded-lg px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||
placeholder="Введите текст сообщения..."></textarea>
|
||
</div>
|
||
<button type="submit" class="w-full bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg transition-colors">
|
||
<i class="fas fa-send mr-2"></i>
|
||
Отправить
|
||
</button>
|
||
</form>
|
||
<div id="send-result" class="mt-4 hidden"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Notification Settings -->
|
||
<div class="bg-white shadow rounded-lg p-6">
|
||
<h3 class="text-lg font-semibold text-gray-900 mb-6">
|
||
<i class="fas fa-bell mr-2 text-purple-500"></i>
|
||
Настройки уведомлений
|
||
</h3>
|
||
|
||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||
<!-- Notification Types -->
|
||
<div>
|
||
<h4 class="font-medium text-gray-900 mb-4">Типы уведомлений</h4>
|
||
<div class="space-y-3">
|
||
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
|
||
<div class="flex items-center">
|
||
<i class="fas fa-envelope mr-3 text-blue-500"></i>
|
||
<span class="text-sm text-gray-900">Новые обращения</span>
|
||
</div>
|
||
<div class="w-3 h-3 bg-green-500 rounded-full"></div>
|
||
</div>
|
||
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
|
||
<div class="flex items-center">
|
||
<i class="fas fa-calculator mr-3 text-green-500"></i>
|
||
<span class="text-sm text-gray-900">Расчеты стоимости</span>
|
||
</div>
|
||
<div class="w-3 h-3 bg-green-500 rounded-full"></div>
|
||
</div>
|
||
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
|
||
<div class="flex items-center">
|
||
<i class="fas fa-briefcase mr-3 text-purple-500"></i>
|
||
<span class="text-sm text-gray-900">Новые проекты</span>
|
||
</div>
|
||
<div class="w-3 h-3 bg-green-500 rounded-full"></div>
|
||
</div>
|
||
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
|
||
<div class="flex items-center">
|
||
<i class="fas fa-cog mr-3 text-orange-500"></i>
|
||
<span class="text-sm text-gray-900">Новые услуги</span>
|
||
</div>
|
||
<div class="w-3 h-3 bg-green-500 rounded-full"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Bot Information -->
|
||
<div>
|
||
<h4 class="font-medium text-gray-900 mb-4">Информация о боте</h4>
|
||
<div class="space-y-3">
|
||
<div class="flex justify-between">
|
||
<span class="text-sm text-gray-600">Статус:</span>
|
||
<span class="text-sm font-medium text-green-600">Активен</span>
|
||
</div>
|
||
<div class="flex justify-between">
|
||
<span class="text-sm text-gray-600">Токен:</span>
|
||
<span class="text-sm font-mono text-gray-800">••••••••••</span>
|
||
</div>
|
||
<div class="flex justify-between">
|
||
<span class="text-sm text-gray-600">Chat ID:</span>
|
||
<span class="text-sm font-mono text-gray-800">••••••••••</span>
|
||
</div>
|
||
<div class="flex justify-between">
|
||
<span class="text-sm text-gray-600">Последнее уведомление:</span>
|
||
<span class="text-sm text-gray-600">Недавно</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Recent Notifications -->
|
||
<div class="bg-white shadow rounded-lg p-6">
|
||
<h3 class="text-lg font-semibold text-gray-900 mb-6">
|
||
<i class="fas fa-history mr-2 text-gray-500"></i>
|
||
Недавние уведомления
|
||
</h3>
|
||
|
||
<div class="text-center py-8 text-gray-500">
|
||
<i class="fas fa-inbox text-4xl text-gray-300 mb-4"></i>
|
||
<p>Уведомления будут отображаться здесь после отправки</p>
|
||
</div>
|
||
</div>
|
||
<% } %>
|
||
</div>
|
||
</main>
|
||
</div>
|
||
|
||
<!-- JavaScript -->
|
||
<script src="/js/main.js"></script>
|
||
|
||
<script>
|
||
// Bot Configuration
|
||
document.getElementById('bot-config-form').addEventListener('submit', async (e) => {
|
||
e.preventDefault();
|
||
|
||
const formData = new FormData(e.target);
|
||
const data = Object.fromEntries(formData);
|
||
|
||
try {
|
||
showLoading('Сохранение настроек...');
|
||
|
||
const response = await fetch('/admin/telegram/configure', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify(data)
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
showResult('config-result', result.message, 'success');
|
||
// Update bot info if available
|
||
if (result.botInfo) {
|
||
updateBotInfo(result.botInfo);
|
||
}
|
||
if (result.availableChats) {
|
||
updateAvailableChats(result.availableChats);
|
||
}
|
||
} else {
|
||
showResult('config-result', result.message, 'error');
|
||
}
|
||
} catch (error) {
|
||
showResult('config-result', 'Ошибка при сохранении настроек', 'error');
|
||
} finally {
|
||
hideLoading();
|
||
}
|
||
});
|
||
|
||
// Send message form
|
||
document.getElementById('send-message-form').addEventListener('submit', async (e) => {
|
||
e.preventDefault();
|
||
|
||
const formData = new FormData(e.target);
|
||
const message = formData.get('message');
|
||
const chatIds = formData.getAll('chatIds');
|
||
const parseMode = formData.get('parseMode');
|
||
const disableWebPagePreview = formData.has('disableWebPagePreview');
|
||
const disableNotification = formData.has('disableNotification');
|
||
|
||
if (!message.trim()) {
|
||
showResult('send-result', 'Введите текст сообщения', 'error');
|
||
return;
|
||
}
|
||
|
||
const data = {
|
||
message: message.trim(),
|
||
chatIds: chatIds.length > 0 ? chatIds : [],
|
||
parseMode,
|
||
disableWebPagePreview,
|
||
disableNotification
|
||
};
|
||
|
||
try {
|
||
const btn = document.getElementById('sendMessageBtn');
|
||
btn.disabled = true;
|
||
btn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>Отправка...';
|
||
|
||
const response = await fetch('/admin/telegram/send', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify(data)
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
showResult('send-result', result.message, 'success');
|
||
document.getElementById('messageText').value = '';
|
||
} else {
|
||
showResult('send-result', result.message, 'error');
|
||
}
|
||
} catch (error) {
|
||
showResult('send-result', 'Ошибка при отправке сообщения', 'error');
|
||
} finally {
|
||
const btn = document.getElementById('sendMessageBtn');
|
||
btn.disabled = false;
|
||
btn.innerHTML = '<i class="fas fa-paper-plane mr-2"></i>Отправить сообщение';
|
||
}
|
||
});
|
||
|
||
// Utility functions
|
||
function toggleTokenVisibility() {
|
||
const input = document.getElementById('botToken');
|
||
const eye = document.getElementById('token-eye');
|
||
|
||
if (input.type === 'password') {
|
||
input.type = 'text';
|
||
eye.className = 'fas fa-eye-slash text-gray-400';
|
||
} else {
|
||
input.type = 'password';
|
||
eye.className = 'fas fa-eye text-gray-400';
|
||
}
|
||
}
|
||
|
||
async function getBotInfo() {
|
||
try {
|
||
showLoading('Получение информации о боте...');
|
||
|
||
const response = await fetch('/admin/telegram/info');
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
updateBotInfo(result.botInfo);
|
||
updateAvailableChats(result.availableChats);
|
||
showResult('config-result', 'Информация о боте обновлена', 'success');
|
||
} else {
|
||
showResult('config-result', result.message, 'error');
|
||
}
|
||
} catch (error) {
|
||
showResult('config-result', 'Ошибка при получении информации о боте', 'error');
|
||
} finally {
|
||
hideLoading();
|
||
}
|
||
}
|
||
|
||
async function refreshBotInfo() {
|
||
await getBotInfo();
|
||
}
|
||
|
||
async function discoverChats() {
|
||
try {
|
||
showLoading('Поиск доступных чатов...');
|
||
|
||
const response = await fetch('/admin/telegram/info');
|
||
const result = await response.json();
|
||
|
||
if (result.success && result.availableChats) {
|
||
updateAvailableChats(result.availableChats);
|
||
showNotification('Найдено чатов: ' + result.availableChats.length, 'success');
|
||
} else {
|
||
showNotification('Чаты не найдены', 'warning');
|
||
}
|
||
} catch (error) {
|
||
showNotification('Ошибка при поиске чатов', 'error');
|
||
} finally {
|
||
hideLoading();
|
||
}
|
||
}
|
||
|
||
function selectChat(chatId, chatTitle) {
|
||
document.getElementById('chatId').value = chatId;
|
||
showNotification(`Выбран чат: ${chatTitle}`, 'success');
|
||
}
|
||
|
||
async function testConnection() {
|
||
try {
|
||
showLoading('Тестирование соединения...');
|
||
|
||
const response = await fetch('/admin/telegram/test', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
}
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
showResult('send-result', 'Соединение успешно! Тестовое сообщение отправлено.', 'success');
|
||
} else {
|
||
showResult('send-result', result.message, 'error');
|
||
}
|
||
} catch (error) {
|
||
showResult('send-result', 'Ошибка при тестировании соединения', 'error');
|
||
} finally {
|
||
hideLoading();
|
||
}
|
||
}
|
||
|
||
function previewMessage() {
|
||
const message = document.getElementById('messageText').value;
|
||
const parseMode = document.getElementById('parseMode').value;
|
||
|
||
if (!message.trim()) {
|
||
showNotification('Введите текст сообщения для предпросмотра', 'warning');
|
||
return;
|
||
}
|
||
|
||
// Simple preview modal (you can enhance this)
|
||
const preview = window.open('', 'preview', 'width=400,height=300');
|
||
preview.document.write(`
|
||
<html>
|
||
<head><title>Предпросмотр сообщения</title></head>
|
||
<body style="font-family: Arial, sans-serif; padding: 20px;">
|
||
<h3>Предпросмотр сообщения (${parseMode || 'Обычный текст'})</h3>
|
||
<div style="border: 1px solid #ccc; padding: 10px; background: #f9f9f9;">
|
||
${parseMode === 'HTML' ? message : message.replace(/\n/g, '<br>')}
|
||
</div>
|
||
<button onclick="window.close()" style="margin-top: 10px;">Закрыть</button>
|
||
</body>
|
||
</html>
|
||
`);
|
||
}
|
||
|
||
// UI Helper functions
|
||
function updateBotInfo(botInfo) {
|
||
const container = document.getElementById('bot-info-container');
|
||
if (!botInfo) return;
|
||
|
||
container.innerHTML = `
|
||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||
<div class="bg-gray-50 p-4 rounded-lg">
|
||
<div class="text-sm text-gray-600">Имя бота</div>
|
||
<div class="text-lg font-semibold text-gray-900">@${botInfo.username}</div>
|
||
</div>
|
||
<div class="bg-gray-50 p-4 rounded-lg">
|
||
<div class="text-sm text-gray-600">Отображаемое имя</div>
|
||
<div class="text-lg font-semibold text-gray-900">${botInfo.first_name}</div>
|
||
</div>
|
||
<div class="bg-gray-50 p-4 rounded-lg">
|
||
<div class="text-sm text-gray-600">ID бота</div>
|
||
<div class="text-lg font-semibold text-gray-900">${botInfo.id}</div>
|
||
</div>
|
||
<div class="bg-gray-50 p-4 rounded-lg">
|
||
<div class="text-sm text-gray-600">Может читать сообщения</div>
|
||
<div class="text-lg font-semibold ${botInfo.can_read_all_group_messages ? 'text-green-600' : 'text-red-600'}">
|
||
${botInfo.can_read_all_group_messages ? 'Да' : 'Нет'}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
function updateAvailableChats(chats) {
|
||
const container = document.getElementById('available-chats-container');
|
||
if (!chats || chats.length === 0) {
|
||
container.innerHTML = `
|
||
<div class="text-center py-8 text-gray-500">
|
||
<i class="fas fa-comments text-4xl text-gray-300 mb-4"></i>
|
||
<p class="mb-2">Чаты не найдены</p>
|
||
<p class="text-sm">Отправьте боту сообщение или добавьте его в группу, затем нажмите "Найти чаты"</p>
|
||
</div>
|
||
`;
|
||
return;
|
||
}
|
||
|
||
const chatsHtml = chats.map(chat => `
|
||
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
|
||
<div class="flex items-center space-x-3">
|
||
<div class="w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center">
|
||
<i class="fas ${chat.type === 'group' || chat.type === 'supergroup' ? 'fa-users' : 'fa-user'} text-blue-600 text-sm"></i>
|
||
</div>
|
||
<div>
|
||
<div class="font-medium text-gray-900">${chat.title}</div>
|
||
<div class="text-sm text-gray-500">
|
||
${chat.type} • ID: ${chat.id}
|
||
${chat.username ? '• @' + chat.username : ''}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<button onclick="selectChat('${chat.id}', '${chat.title}')"
|
||
class="text-blue-600 hover:text-blue-800 text-sm">
|
||
Выбрать
|
||
</button>
|
||
</div>
|
||
`).join('');
|
||
|
||
container.innerHTML = `<div class="grid gap-3">${chatsHtml}</div>`;
|
||
|
||
// Also update recipients list
|
||
updateRecipientsList(chats);
|
||
}
|
||
|
||
function updateRecipientsList(chats) {
|
||
const container = document.getElementById('recipients-container');
|
||
if (!chats || chats.length === 0) {
|
||
container.innerHTML = `
|
||
<div class="text-sm text-gray-500">
|
||
<i class="fas fa-info-circle mr-1"></i>
|
||
Сообщение будет отправлено в чат по умолчанию
|
||
</div>
|
||
`;
|
||
return;
|
||
}
|
||
|
||
const recipientsHtml = chats.map(chat => `
|
||
<label class="flex items-center">
|
||
<input type="checkbox" name="chatIds" value="${chat.id}"
|
||
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded">
|
||
<span class="ml-2 text-sm text-gray-900">
|
||
${chat.title}
|
||
<span class="text-gray-500">(${chat.type})</span>
|
||
</span>
|
||
</label>
|
||
`).join('');
|
||
|
||
container.innerHTML = recipientsHtml;
|
||
}
|
||
|
||
function showResult(elementId, message, type) {
|
||
const element = document.getElementById(elementId);
|
||
const className = type === 'success' ? 'bg-green-100 text-green-800' :
|
||
type === 'warning' ? 'bg-yellow-100 text-yellow-800' :
|
||
'bg-red-100 text-red-800';
|
||
const icon = type === 'success' ? 'fa-check' :
|
||
type === 'warning' ? 'fa-exclamation-triangle' :
|
||
'fa-times';
|
||
|
||
element.className = `mt-4 p-3 rounded-lg ${className}`;
|
||
element.innerHTML = `<i class="fas ${icon} mr-2"></i>${message}`;
|
||
element.classList.remove('hidden');
|
||
|
||
setTimeout(() => {
|
||
element.classList.add('hidden');
|
||
}, 5000);
|
||
}
|
||
|
||
function showNotification(message, type = 'info') {
|
||
const notification = document.createElement('div');
|
||
notification.className = `fixed top-4 right-4 z-50 px-4 py-3 rounded-lg shadow-lg text-white max-w-sm transform transition-all duration-300 translate-x-full`;
|
||
|
||
switch(type) {
|
||
case 'success':
|
||
notification.classList.add('bg-green-600');
|
||
break;
|
||
case 'error':
|
||
notification.classList.add('bg-red-600');
|
||
break;
|
||
case 'warning':
|
||
notification.classList.add('bg-yellow-600');
|
||
break;
|
||
default:
|
||
notification.classList.add('bg-blue-600');
|
||
}
|
||
|
||
notification.innerHTML = `
|
||
<div class="flex items-center">
|
||
<i class="fas ${type === 'success' ? 'fa-check-circle' : type === 'error' ? 'fa-exclamation-circle' : type === 'warning' ? 'fa-exclamation-triangle' : 'fa-info-circle'} mr-2"></i>
|
||
<span>${message}</span>
|
||
</div>
|
||
`;
|
||
|
||
document.body.appendChild(notification);
|
||
|
||
setTimeout(() => {
|
||
notification.classList.remove('translate-x-full');
|
||
}, 100);
|
||
|
||
setTimeout(() => {
|
||
notification.classList.add('translate-x-full');
|
||
setTimeout(() => {
|
||
if (document.body.contains(notification)) {
|
||
document.body.removeChild(notification);
|
||
}
|
||
}, 300);
|
||
}, 4000);
|
||
}
|
||
|
||
function showLoading(message) {
|
||
// Create or show loading overlay
|
||
let overlay = document.getElementById('loading-overlay');
|
||
if (!overlay) {
|
||
overlay = document.createElement('div');
|
||
overlay.id = 'loading-overlay';
|
||
overlay.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50';
|
||
overlay.innerHTML = `
|
||
<div class="bg-white p-6 rounded-lg shadow-lg">
|
||
<div class="flex items-center">
|
||
<i class="fas fa-spinner fa-spin text-blue-600 text-xl mr-3"></i>
|
||
<span id="loading-message">${message}</span>
|
||
</div>
|
||
</div>
|
||
`;
|
||
document.body.appendChild(overlay);
|
||
} else {
|
||
document.getElementById('loading-message').textContent = message;
|
||
overlay.style.display = 'flex';
|
||
}
|
||
}
|
||
|
||
function hideLoading() {
|
||
const overlay = document.getElementById('loading-overlay');
|
||
if (overlay) {
|
||
overlay.style.display = 'none';
|
||
}
|
||
}
|
||
</script>
|
||
</body>
|
||
</html> |