Files
sst_site/.history/views/admin/telegram_20251026214715.ejs
2025-10-26 22:14:47 +09:00

1281 lines
63 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- Content Header (Page header) --><!DOCTYPE html>
<section class="content-header"><html lang="ru">
<div class="container-fluid"><head>
<div class="row mb-2"> <meta charset="UTF-8">
<div class="col-sm-6"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<h1><i class="fab fa-telegram mr-2 text-info"></i>Telegram Bot</h1> <title>Telegram Bot - SmartSolTech Admin</title>
</div>
<div class="col-sm-6"> <!-- Tailwind CSS -->
<ol class="breadcrumb float-sm-right"> <script src="https://cdn.tailwindcss.com"></script>
<li class="breadcrumb-item"><a href="/admin">Админ</a></li>
<li class="breadcrumb-item active">Telegram Bot</li> <!-- Font Awesome -->
</ol> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
</div>
</div> <!-- Custom CSS -->
</div> <link rel="stylesheet" href="/css/main.css">
</section> <link rel="stylesheet" href="/css/fixes.css">
</head>
<!-- Main content --><body class="bg-gray-100">
<section class="content">
<div class="container-fluid"> <!-- Main Content -->
<!-- Bot Status Card --> <main class="flex-1 p-8">
<div class="card"> <div class="space-y-6">
<div class="card-header"> <!-- Header -->
<h3 class="card-title">Статус бота</h3> <div class="bg-white shadow rounded-lg p-6">
<div class="card-tools"> <div class="flex items-center justify-between">
<button id="refresh-status" class="btn btn-sm btn-default"> <div>
<i class="fas fa-sync-alt"></i> <h1 class="text-2xl font-bold text-gray-900 flex items-center">
</button> <i class="fab fa-telegram mr-3 text-blue-500"></i>
</div> Telegram Bot
</div> </h1>
<div class="card-body"> <p class="mt-2 text-gray-600">Настройка и управление уведомлениями через Telegram</p>
<div class="row"> </div>
<div class="col-md-6"> <div class="flex items-center space-x-3">
<div class="info-box"> <div class="flex items-center">
<span class="info-box-icon" id="status-icon"> <div class="w-3 h-3 rounded-full <%= botConfigured ? 'bg-green-500' : 'bg-red-500' %> mr-2"></div>
<i class="fas fa-question"></i> <span class="text-sm font-medium <%= botConfigured ? 'text-green-700' : 'text-red-700' %>">
</span> <%= botConfigured ? 'Подключен' : 'Не настроен' %>
<div class="info-box-content"> </span>
<span class="info-box-text">Статус подключения</span> </div>
<span class="info-box-number" id="status-text">Проверка...</span> </div>
</div> </div>
</div> </div>
</div>
<div class="col-md-6"> <!-- Bot Configuration -->
<div class="info-box"> <div class="bg-white shadow rounded-lg p-6">
<span class="info-box-icon bg-info"> <h3 class="text-lg font-semibold text-gray-900 mb-6">
<i class="fab fa-telegram"></i> <i class="fas fa-cog mr-2 text-blue-500"></i>
</span> Конфигурация бота
<div class="info-box-content"> </h3>
<span class="info-box-text">Имя бота</span>
<span class="info-box-number" id="bot-name">Загрузка...</span> <form id="bot-config-form" class="space-y-6">
</div> <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
</div> <!-- Bot Token -->
</div> <div>
</div> <label for="botToken" class="block text-sm font-medium text-gray-700 mb-2">
</div> Токен бота *
</div> </label>
<div class="relative">
<!-- Configuration Card --> <input type="password" id="botToken" name="botToken"
<div class="card"> value="<%= botToken %>"
<div class="card-header"> placeholder="1234567890:ABCDEFGHIJKLMNOPQRSTUVWXYZ"
<h3 class="card-title">Конфигурация</h3> 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">
</div> <button type="button" onclick="toggleTokenVisibility()"
<form id="config-form"> class="absolute inset-y-0 right-0 pr-3 flex items-center">
<div class="card-body"> <i id="token-eye" class="fas fa-eye text-gray-400"></i>
<div class="form-group"> </button>
<label for="bot-token">Токен бота</label> </div>
<div class="input-group"> <p class="mt-1 text-sm text-gray-500">
<input type="password" class="form-control" id="bot-token" name="botToken" placeholder="Введите токен бота"> Получите токен от <a href="https://t.me/BotFather" target="_blank" class="text-blue-600 underline">@BotFather</a>
<div class="input-group-append"> </p>
<button type="button" class="btn btn-outline-secondary" onclick="toggleTokenVisibility()"> </div>
<i class="fas fa-eye" id="token-eye"></i>
</button> <!-- Default Chat ID -->
</div> <div>
</div> <label for="chatId" class="block text-sm font-medium text-gray-700 mb-2">
<small class="form-text text-muted"> ID чата по умолчанию
Получите токен у <a href="https://t.me/BotFather" target="_blank">@BotFather</a> </label>
</small> <input type="text" id="chatId" name="chatId"
</div> value="<%= chatId %>"
placeholder="-1001234567890"
<div class="form-group"> 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">
<label for="admin-chat-id">ID чата администратора</label> <p class="mt-1 text-sm text-gray-500">
<input type="text" class="form-control" id="admin-chat-id" name="adminChatId" placeholder="Введите ID чата"> Оставьте пустым, если будете выбирать чат из списка
<small class="form-text text-muted"> </p>
Узнайте ваш ID чата у <a href="https://t.me/userinfobot" target="_blank">@userinfobot</a> </div>
</small> </div>
</div>
<div class="flex space-x-3">
<div class="form-group"> <button type="submit" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg transition-colors">
<div class="custom-control custom-switch"> <i class="fas fa-save mr-2"></i>
<input type="checkbox" class="custom-control-input" id="notifications-enabled" name="notificationsEnabled"> Сохранить настройки
<label class="custom-control-label" for="notifications-enabled">Включить уведомления</label> </button>
</div> <button type="button" onclick="getBotInfo()" class="bg-gray-600 hover:bg-gray-700 text-white px-4 py-2 rounded-lg transition-colors">
</div> <i class="fas fa-info-circle mr-2"></i>
Получить информацию о боте
<div class="form-group"> </button>
<label>Типы уведомлений</label> </div>
<div class="row"> </form>
<div class="col-md-6">
<div class="custom-control custom-checkbox"> <div id="config-result" class="mt-4 hidden"></div>
<input type="checkbox" class="custom-control-input" id="notify-contacts" name="notifyContacts"> </div>
<label class="custom-control-label" for="notify-contacts">Новые контакты</label>
</div> <% if (botConfigured) { %>
<div class="custom-control custom-checkbox"> <!-- Bot Information -->
<input type="checkbox" class="custom-control-input" id="notify-orders" name="notifyOrders"> <div class="bg-white shadow rounded-lg p-6">
<label class="custom-control-label" for="notify-orders">Новые заказы</label> <div class="flex items-center justify-between mb-6">
</div> <h3 class="text-lg font-semibold text-gray-900">
</div> <i class="fas fa-robot mr-2 text-green-500"></i>
<div class="col-md-6"> Информация о боте
<div class="custom-control custom-checkbox"> </h3>
<input type="checkbox" class="custom-control-input" id="notify-errors" name="notifyErrors"> <button onclick="refreshBotInfo()" class="text-blue-600 hover:text-blue-800 text-sm">
<label class="custom-control-label" for="notify-errors">Системные ошибки</label> <i class="fas fa-refresh mr-1"></i>
</div> Обновить
<div class="custom-control custom-checkbox"> </button>
<input type="checkbox" class="custom-control-input" id="notify-updates" name="notifyUpdates"> </div>
<label class="custom-control-label" for="notify-updates">Обновления системы</label>
</div> <div id="bot-info-container">
</div> <% if (botInfo) { %>
</div> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
</div> <div class="bg-gray-50 p-4 rounded-lg">
</div> <div class="text-sm text-gray-600">Имя бота</div>
<div class="card-footer"> <div class="text-lg font-semibold text-gray-900">@<%= botInfo.username %></div>
<button type="submit" class="btn btn-primary"> </div>
<i class="fas fa-save mr-1"></i>Сохранить <div class="bg-gray-50 p-4 rounded-lg">
</button> <div class="text-sm text-gray-600">Отображаемое имя</div>
<button type="button" class="btn btn-secondary ml-2" onclick="testConnection()"> <div class="text-lg font-semibold text-gray-900"><%= botInfo.first_name %></div>
<i class="fas fa-bolt mr-1"></i>Проверить подключение </div>
</button> <div class="bg-gray-50 p-4 rounded-lg">
</div> <div class="text-sm text-gray-600">ID бота</div>
</form> <div class="text-lg font-semibold text-gray-900"><%= botInfo.id %></div>
</div> </div>
<div class="bg-gray-50 p-4 rounded-lg">
<!-- Test Message Card --> <div class="text-sm text-gray-600">Может читать сообщения</div>
<div class="card"> <div class="text-lg font-semibold <%= botInfo.can_read_all_group_messages ? 'text-green-600' : 'text-red-600' %>">
<div class="card-header"> <%= botInfo.can_read_all_group_messages ? 'Да' : 'Нет' %>
<h3 class="card-title">Тестовое сообщение</h3> </div>
</div> </div>
<div class="card-body"> </div>
<div class="form-group"> <% } else { %>
<label for="test-message">Сообщение</label> <div class="text-center py-4 text-gray-500">
<textarea class="form-control" id="test-message" rows="3" placeholder="Введите тестовое сообщение...">Тестовое сообщение от SmartSolTech Admin Panel</textarea> <i class="fas fa-robot text-4xl text-gray-300 mb-2"></i>
</div> <p>Настройте токен бота для получения информации</p>
<button type="button" class="btn btn-info" onclick="sendTestMessage()"> </div>
<i class="fas fa-paper-plane mr-1"></i>Отправить тест <% } %>
</button> </div>
</div> </div>
</div>
<!-- Available Chats -->
<!-- Webhook Configuration Card --> <div class="bg-white shadow rounded-lg p-6">
<div class="card"> <div class="flex items-center justify-between mb-6">
<div class="card-header"> <h3 class="text-lg font-semibold text-gray-900">
<h3 class="card-title">Настройка Webhook</h3> <i class="fas fa-comments mr-2 text-blue-500"></i>
</div> Доступные чаты
<div class="card-body"> </h3>
<div class="form-group"> <button onclick="discoverChats()" class="bg-blue-600 hover:bg-blue-700 text-white px-3 py-1 rounded text-sm transition-colors">
<label for="webhook-url">URL Webhook</label> <i class="fas fa-search mr-1"></i>
<div class="input-group"> Найти чаты
<input type="url" class="form-control" id="webhook-url" readonly> </button>
<div class="input-group-append"> </div>
<button type="button" class="btn btn-outline-secondary" onclick="copyWebhookUrl()">
<i class="fas fa-copy"></i> <div id="available-chats-container">
</button> <% if (availableChats && availableChats.length > 0) { %>
</div> <div class="grid gap-3">
</div> <% availableChats.forEach(chat => { %>
</div> <div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
<div class="row"> <div class="flex items-center space-x-3">
<div class="col-md-6"> <div class="w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center">
<button type="button" class="btn btn-success btn-block" onclick="setWebhook()"> <i class="fas <%= chat.type === 'group' || chat.type === 'supergroup' ? 'fa-users' : 'fa-user' %> text-blue-600 text-sm"></i>
<i class="fas fa-link mr-1"></i>Установить Webhook </div>
</button> <div>
</div> <div class="font-medium text-gray-900"><%= chat.title %></div>
<div class="col-md-6"> <div class="text-sm text-gray-500">
<button type="button" class="btn btn-warning btn-block" onclick="deleteWebhook()"> <%= chat.type %> • ID: <%= chat.id %>
<i class="fas fa-unlink mr-1"></i>Удалить Webhook <% if (chat.username) { %>• @<%= chat.username %><% } %>
</button> </div>
</div> </div>
</div> </div>
<div class="mt-3"> <button onclick="selectChat('<%= chat.id %>', '<%= chat.title %>')"
<small class="text-muted"> class="text-blue-600 hover:text-blue-800 text-sm">
Webhook позволяет боту получать обновления в реальном времени. Выбрать
URL автоматически генерируется на основе текущего домена. </button>
</small> </div>
</div> <% }); %>
</div> </div>
</div> <% } else { %>
<div class="text-center py-8 text-gray-500">
<!-- Activity Log Card --> <i class="fas fa-comments text-4xl text-gray-300 mb-4"></i>
<div class="card"> <p class="mb-2">Чаты не найдены</p>
<div class="card-header"> <p class="text-sm">Отправьте боту сообщение или добавьте его в группу, затем нажмите "Найти чаты"</p>
<h3 class="card-title">Журнал активности</h3> </div>
<div class="card-tools"> <% } %>
<button class="btn btn-sm btn-default" onclick="clearLog()"> </div>
<i class="fas fa-trash"></i> Очистить </div>
</button>
</div> <!-- Message Sender -->
</div> <div class="bg-white shadow rounded-lg p-6">
<div class="card-body"> <h3 class="text-lg font-semibold text-gray-900 mb-6">
<div id="activity-log" style="max-height: 300px; overflow-y: auto;"> <i class="fas fa-paper-plane mr-2 text-purple-500"></i>
<!-- Log entries will be added here --> Отправить сообщение
</div> </h3>
</div>
</div> <form id="send-message-form" class="space-y-6">
</div> <!-- Message Content -->
</section> <div>
<label for="messageText" class="block text-sm font-medium text-gray-700 mb-2">
<!-- Scripts --> Текст сообщения *
<script> </label>
let activityLog = []; <textarea id="messageText" name="message" rows="4" required
placeholder="Введите сообщение..."
document.addEventListener('DOMContentLoaded', function() { 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>
loadConfig(); </div>
checkBotStatus();
generateWebhookUrl(); <!-- Recipients -->
initializeLog(); <div>
<label class="block text-sm font-medium text-gray-700 mb-2">
// Set up form submission Получатели
document.getElementById('config-form').addEventListener('submit', saveConfig); </label>
}); <div id="recipients-container" class="space-y-2">
<% if (availableChats && availableChats.length > 0) { %>
function addLog(message, type = 'info') { <% availableChats.forEach(chat => { %>
const timestamp = new Date().toLocaleString('ru-RU'); <label class="flex items-center">
const logEntry = { <input type="checkbox" name="chatIds" value="<%= chat.id %>"
timestamp, class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded">
message, <span class="ml-2 text-sm text-gray-900">
type <%= chat.title %>
}; <span class="text-gray-500">(<%= chat.type %>)</span>
</span>
activityLog.unshift(logEntry); </label>
if (activityLog.length > 100) { <% }); %>
activityLog = activityLog.slice(0, 100); <% } else { %>
} <div class="text-sm text-gray-500">
<i class="fas fa-info-circle mr-1"></i>
updateLogDisplay(); Сообщение будет отправлено в чат по умолчанию
} </div>
<% } %>
function updateLogDisplay() { </div>
const logContainer = document.getElementById('activity-log'); </div>
const typeColors = {
info: 'text-info', <!-- Message Options -->
success: 'text-success', <div class="bg-gray-50 p-4 rounded-lg">
error: 'text-danger', <h4 class="text-sm font-medium text-gray-900 mb-3">Настройки сообщения</h4>
warning: 'text-warning' <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
}; <label class="flex items-center">
<input type="checkbox" name="disableWebPagePreview"
logContainer.innerHTML = activityLog.map(entry => ` class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded">
<div class="mb-2"> <span class="ml-2 text-sm text-gray-900">Отключить предпросмотр ссылок</span>
<small class="text-muted">[${entry.timestamp}]</small> </label>
<span class="${typeColors[entry.type] || 'text-info'}">${entry.message}</span> <label class="flex items-center">
</div> <input type="checkbox" name="disableNotification"
`).join(''); 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>
function initializeLog() { </div>
addLog('Telegram Bot панель загружена'); <div class="mt-4">
} <label for="parseMode" class="block text-sm font-medium text-gray-700 mb-1">
Формат текста
function clearLog() { </label>
if (confirm('Очистить журнал активности?')) { <select name="parseMode" id="parseMode"
activityLog = []; class="block w-full border border-gray-300 rounded-md px-3 py-2 text-sm">
updateLogDisplay(); <option value="HTML">HTML</option>
addLog('Журнал активности очищен'); <option value="Markdown">Markdown</option>
} <option value="">Обычный текст</option>
} </select>
</div>
async function loadConfig() { </div>
try {
const response = await fetch('/api/admin/telegram/config'); <!-- Submit Buttons -->
const data = await response.json(); <div class="flex justify-between">
<button type="button" onclick="previewMessage()"
if (data.success) { 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">
const config = data.config; <i class="fas fa-eye mr-2"></i>
document.getElementById('bot-token').value = config.botToken || ''; Предпросмотр
document.getElementById('admin-chat-id').value = config.adminChatId || ''; </button>
document.getElementById('notifications-enabled').checked = config.notificationsEnabled || false; <div class="space-x-3">
document.getElementById('notify-contacts').checked = config.notifyContacts || false; <button type="button" onclick="testConnection()"
document.getElementById('notify-orders').checked = config.notifyOrders || false; 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">
document.getElementById('notify-errors').checked = config.notifyErrors || false; <i class="fas fa-vial mr-2"></i>
document.getElementById('notify-updates').checked = config.notifyUpdates || false; Тест соединения
</button>
addLog('Конфигурация загружена'); <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">
} catch (error) { <i class="fas fa-paper-plane mr-2"></i>
console.error('Error loading config:', error); Отправить сообщение
addLog('Ошибка загрузки конфигурации: ' + error.message, 'error'); </button>
} </div>
} </div>
</form>
async function saveConfig(event) {
event.preventDefault(); <div id="send-result" class="mt-4 hidden"></div>
</div>
const formData = new FormData(event.target); <!-- Bot Status and Controls -->
const config = { <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
botToken: formData.get('botToken'), <!-- Connection Test -->
adminChatId: formData.get('adminChatId'), <div class="bg-white shadow rounded-lg p-6">
notificationsEnabled: formData.has('notificationsEnabled'), <h3 class="text-lg font-semibold text-gray-900 mb-4">
notifyContacts: formData.has('notifyContacts'), <i class="fas fa-plug mr-2 text-green-500"></i>
notifyOrders: formData.has('notifyOrders'), Проверка подключения
notifyErrors: formData.has('notifyErrors'), </h3>
notifyUpdates: formData.has('notifyUpdates') <p class="text-gray-600 mb-4">
}; Отправить тестовое сообщение для проверки работоспособности бота.
</p>
try { <button id="test-connection" class="w-full bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded-lg transition-colors">
const response = await fetch('/api/admin/telegram/config', { <i class="fas fa-vial mr-2"></i>
method: 'POST', Тестировать подключение
headers: { </button>
'Content-Type': 'application/json' <div id="test-result" class="mt-4 hidden"></div>
}, </div>
body: JSON.stringify(config)
}); <!-- Send Custom Message -->
<div class="bg-white shadow rounded-lg p-6">
const data = await response.json(); <h3 class="text-lg font-semibold text-gray-900 mb-4">
<i class="fas fa-paper-plane mr-2 text-blue-500"></i>
if (data.success) { Отправить сообщение
addLog('Конфигурация сохранена', 'success'); </h3>
// Refresh bot status after saving config <form id="send-message-form">
setTimeout(checkBotStatus, 1000); <div class="mb-4">
} else { <label class="block text-sm font-medium text-gray-700 mb-2">
throw new Error(data.message); Сообщение
} </label>
} catch (error) { <textarea
console.error('Error saving config:', error); id="custom-message"
addLog('Ошибка сохранения конфигурации: ' + error.message, 'error'); 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>
async function checkBotStatus() { <button type="submit" class="w-full bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg transition-colors">
try { <i class="fas fa-send mr-2"></i>
const response = await fetch('/api/admin/telegram/status'); Отправить
const data = await response.json(); </button>
</form>
const statusIcon = document.getElementById('status-icon'); <div id="send-result" class="mt-4 hidden"></div>
const statusText = document.getElementById('status-text'); </div>
const botName = document.getElementById('bot-name'); </div>
if (data.success && data.status.connected) { <!-- Notification Settings -->
statusIcon.className = 'info-box-icon bg-success'; <div class="bg-white shadow rounded-lg p-6">
statusIcon.innerHTML = '<i class="fas fa-check"></i>'; <h3 class="text-lg font-semibold text-gray-900 mb-6">
statusText.textContent = 'Подключен'; <i class="fas fa-bell mr-2 text-purple-500"></i>
botName.textContent = data.status.botInfo.first_name; Настройки уведомлений
addLog('Бот подключен: ' + data.status.botInfo.first_name, 'success'); </h3>
} else {
statusIcon.className = 'info-box-icon bg-danger'; <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
statusIcon.innerHTML = '<i class="fas fa-times"></i>'; <!-- Notification Types -->
statusText.textContent = 'Не подключен'; <div>
botName.textContent = 'Не подключен'; <h4 class="font-medium text-gray-900 mb-4">Типы уведомлений</h4>
addLog('Бот не подключен: ' + (data.message || 'Неизвестная ошибка'), 'error'); <div class="space-y-3">
} <div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
} catch (error) { <div class="flex items-center">
console.error('Error checking bot status:', error); <i class="fas fa-envelope mr-3 text-blue-500"></i>
const statusIcon = document.getElementById('status-icon'); <span class="text-sm text-gray-900">Новые обращения</span>
const statusText = document.getElementById('status-text'); </div>
const botName = document.getElementById('bot-name'); <div class="w-3 h-3 bg-green-500 rounded-full"></div>
</div>
statusIcon.className = 'info-box-icon bg-warning'; <div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
statusIcon.innerHTML = '<i class="fas fa-exclamation"></i>'; <div class="flex items-center">
statusText.textContent = 'Ошибка проверки'; <i class="fas fa-calculator mr-3 text-green-500"></i>
botName.textContent = 'Ошибка'; <span class="text-sm text-gray-900">Расчеты стоимости</span>
addLog('Ошибка проверки статуса: ' + error.message, 'error'); </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">
async function testConnection() { <div class="flex items-center">
addLog('Проверка подключения...'); <i class="fas fa-briefcase mr-3 text-purple-500"></i>
await checkBotStatus(); <span class="text-sm text-gray-900">Новые проекты</span>
} </div>
<div class="w-3 h-3 bg-green-500 rounded-full"></div>
async function sendTestMessage() { </div>
const message = document.getElementById('test-message').value; <div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
if (!message.trim()) { <div class="flex items-center">
alert('Введите сообщение для отправки'); <i class="fas fa-cog mr-3 text-orange-500"></i>
return; <span class="text-sm text-gray-900">Новые услуги</span>
} </div>
<div class="w-3 h-3 bg-green-500 rounded-full"></div>
try { </div>
addLog('Отправка тестового сообщения...'); </div>
</div>
const response = await fetch('/api/admin/telegram/test', {
method: 'POST', <!-- Bot Information -->
headers: { <div>
'Content-Type': 'application/json' <h4 class="font-medium text-gray-900 mb-4">Информация о боте</h4>
}, <div class="space-y-3">
body: JSON.stringify({ message }) <div class="flex justify-between">
}); <span class="text-sm text-gray-600">Статус:</span>
<span class="text-sm font-medium text-green-600">Активен</span>
const data = await response.json(); </div>
<div class="flex justify-between">
if (data.success) { <span class="text-sm text-gray-600">Токен:</span>
addLog('Тестовое сообщение отправлено', 'success'); <span class="text-sm font-mono text-gray-800">••••••••••</span>
} else { </div>
throw new Error(data.message); <div class="flex justify-between">
} <span class="text-sm text-gray-600">Chat ID:</span>
} catch (error) { <span class="text-sm font-mono text-gray-800">••••••••••</span>
console.error('Error sending test message:', error); </div>
addLog('Ошибка отправки сообщения: ' + error.message, 'error'); <div class="flex justify-between">
} <span class="text-sm text-gray-600">Последнее уведомление:</span>
} <span class="text-sm text-gray-600">Недавно</span>
</div>
function generateWebhookUrl() { </div>
const protocol = window.location.protocol; </div>
const host = window.location.host; </div>
const webhookUrl = `${protocol}//${host}/api/telegram/webhook`; </div>
document.getElementById('webhook-url').value = webhookUrl;
} <!-- Recent Notifications -->
<div class="bg-white shadow rounded-lg p-6">
async function setWebhook() { <h3 class="text-lg font-semibold text-gray-900 mb-6">
try { <i class="fas fa-history mr-2 text-gray-500"></i>
addLog('Установка webhook...'); Недавние уведомления
</h3>
const response = await fetch('/api/admin/telegram/webhook', {
method: 'POST' <div class="text-center py-8 text-gray-500">
}); <i class="fas fa-inbox text-4xl text-gray-300 mb-4"></i>
<p>Уведомления будут отображаться здесь после отправки</p>
const data = await response.json(); </div>
</div>
if (data.success) { <% } %>
addLog('Webhook установлен', 'success'); </div>
} else { </main>
throw new Error(data.message); </div>
}
} catch (error) { <!-- JavaScript -->
console.error('Error setting webhook:', error); <script src="/js/main.js"></script>
addLog('Ошибка установки webhook: ' + error.message, 'error');
} <script>
} // Bot Configuration
document.getElementById('bot-config-form').addEventListener('submit', async (e) => {
async function deleteWebhook() { e.preventDefault();
try {
addLog('Удаление webhook...'); const formData = new FormData(e.target);
const data = Object.fromEntries(formData);
const response = await fetch('/api/admin/telegram/webhook', {
method: 'DELETE' try {
}); showLoading('Сохранение настроек...');
const data = await response.json(); const response = await fetch('/admin/telegram/configure', {
method: 'POST',
if (data.success) { headers: {
addLog('Webhook удален', 'success'); 'Content-Type': 'application/json'
} else { },
throw new Error(data.message); body: JSON.stringify(data)
} });
} catch (error) {
console.error('Error deleting webhook:', error); const result = await response.json();
addLog('Ошибка удаления webhook: ' + error.message, 'error');
} if (result.success) {
} showResult('config-result', result.message, 'success');
// Update bot info if available
function copyWebhookUrl() { if (result.botInfo) {
const webhookInput = document.getElementById('webhook-url'); updateBotInfo(result.botInfo);
webhookInput.select(); }
document.execCommand('copy'); if (result.availableChats) {
updateAvailableChats(result.availableChats);
addLog('URL webhook скопирован в буфер обмена'); }
} } else {
showResult('config-result', result.message, 'error');
function toggleTokenVisibility() { }
const tokenInput = document.getElementById('bot-token'); } catch (error) {
const tokenEye = document.getElementById('token-eye'); showResult('config-result', 'Ошибка при сохранении настроек', 'error');
} finally {
if (tokenInput.type === 'password') { hideLoading();
tokenInput.type = 'text'; }
tokenEye.className = 'fas fa-eye-slash'; });
} else {
tokenInput.type = 'password'; // Send message form
tokenEye.className = 'fas fa-eye'; document.getElementById('send-message-form').addEventListener('submit', async (e) => {
} e.preventDefault();
}
</script> 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>