domovoy_bot/web/templates/broadcast.html
Admin cef2ac3714 v1.19 ИГ рассылка + объявления 14 дней + настройки
🔥 ОБЪЯВЛЕНИЯ:
• Срок действия: 7 → 14 дней

🎯 ИНИЦИАТИВНАЯ ГРУППА:
• Добавлена кнопка 'Рассылка ИГ' в меню
• Команда /ig_broadcast для членов ИГ
• Поддержка текста + фото + файлов
• Настройка лимита в веб-панели

⚙️ ВЕБ-ПАНЕЛЬ:
• Новая страница /settings
• Настройка лимита рассылки ИГ (секунды)
• Кнопки статистики фильтруют пользователей
• Индикатор активного фильтра

🎨 UI:
• Исправлен <font> → emoji (Telegram не поддерживает)
• Цветные заголовки через emoji

📁 ФАЙЛЫ:
• handlers/ads.py: 14 дней
• handlers/initiative_group.py: рассылка ИГ
• web/app.py: API настроек
• web/templates/settings.html: страница настроек
• utils/ui_styles.py: без <font> тегов
• database: settings table

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-03-10 17:01:30 +00:00

184 lines
10 KiB
HTML
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.

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Рассылки - Домовой Бот</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css" rel="stylesheet">
<style>
.sidebar { min-height: 100vh; background: linear-gradient(180deg, #2c3e50 0%, #1a252f 100%); }
.sidebar .nav-link { color: rgba(255,255,255,0.8); padding: 1rem; }
.sidebar .nav-link:hover, .sidebar .nav-link.active { color: white; background-color: rgba(255,255,255,0.1); }
.content { padding: 2rem; }
#preview { max-width: 400px; margin-top: 1rem; display: none; }
#preview img { max-width: 100%; border-radius: 8px; }
</style>
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-md-2 sidebar p-0">
<div class="p-3 text-white text-center">
<h4><i class="bi bi-house-door"></i> Домовой Бот</h4>
</div>
<nav class="nav flex-column">
<a class="nav-link" href="/"><i class="bi bi-speedometer2"></i> Дашборд</a>
<a class="nav-link" href="/users"><i class="bi bi-people"></i> Пользователи</a>
<a class="nav-link" href="/verification"><i class="bi bi-shield-check"></i> Верификация</a>
<a class="nav-link" href="/phones"><i class="bi bi-telephone"></i> Телефоны</a>
<a class="nav-link active" href="/broadcast"><i class="bi bi-broadcast"></i> Рассылки</a>
<a class="nav-link" href="/polls"><i class="bi bi-bar-chart"></i> Опросы</a>
<a class="nav-link" href="/events"><i class="bi bi-calendar-event"></i> События</a>
<a class="nav-link" href="/schedules"><i class="bi bi-clock"></i> Расписания</a>
<a class="nav-link" href="/export"><i class="bi bi-download"></i> Экспорт</a>
<a class="nav-link" href="/settings"><i class="bi bi-gear"></i> Настройки</a>
</nav>
</div>
<div class="col-md-10 content">
<h2 class="mb-4"><i class="bi bi-broadcast"></i> Рассылка объявлений</h2>
<div class="card">
<div class="card-header bg-primary text-white">
<h5 class="mb-0"><i class="bi bi-send"></i> Создать рассылку</h5>
</div>
<div class="card-body">
<form id="broadcastForm" enctype="multipart/form-data">
<div class="mb-3">
<label class="form-label">Текст объявления</label>
<textarea class="form-control" id="broadcastText" rows="5"
placeholder="Введите текст объявления..." required></textarea>
<small class="text-muted">Поддерживается HTML: &lt;b&gt;жирный&lt;/b&gt;, &lt;i&gt;курсив&lt;/i&gt;</small>
</div>
<div class="mb-3">
<label class="form-label">Картинка (необязательно)</label>
<input type="file" class="form-control" id="broadcastPhoto" accept="image/*"
onchange="previewImage()">
<small class="text-muted">JPG, PNG до 5MB</small>
<div id="preview">
<img id="previewImg" alt="Предпросмотр">
</div>
</div>
<div class="mb-3">
<label class="form-label">Кому отправить:</label>
<select class="form-select" id="broadcastRecipients">
<option value="all_verified">✅ Всем верифицированным</option>
<option value="ig_only">🔵 Только Инициативной Группе</option>
<option value="chat_only">📢 Только в общий чат</option>
<option value="all_and_chat">✅ Всем + в чат (по умолчанию)</option>
</select>
<small class="text-muted">Выберите получателей рассылки</small>
</div>
<div class="alert alert-info">
<i class="bi bi-info-circle"></i>
<b>Получатели:</b> <span id="recipientsInfo">Все верифицированные пользователи + общий чат</span>
</div>
<button type="submit" class="btn btn-primary btn-lg">
<i class="bi bi-send"></i> Отправить рассылку
</button>
</form>
<div id="result" class="mt-4"></div>
</div>
</div>
<div class="card mt-4">
<div class="card-body">
<h5>Как использовать:</h5>
<ol>
<li>Введите текст объявления</li>
<li>Прикрепите картинку (необязательно)</li>
<li>Нажмите "Отправить рассылку"</li>
<li>Бот отправит всем верифицированным пользователям в личку</li>
<li>Также отправит в общий чат</li>
</ol>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
// Обновление информации о получателях
document.getElementById('broadcastRecipients').addEventListener('change', function() {
const info = {
'all_verified': 'Все верифицированные пользователи (в личку)',
'ig_only': 'Только участники Инициативной Группы (в личку)',
'chat_only': 'Только в общий чат (одно сообщение)',
'all_and_chat': 'Все верифицированные (в личку) + общий чат'
};
document.getElementById('recipientsInfo').textContent = info[this.value] || '';
});
function previewImage() {
const file = document.getElementById('broadcastPhoto').files[0];
const preview = document.getElementById('preview');
const previewImg = document.getElementById('previewImg');
if (file) {
const reader = new FileReader();
reader.onload = function(e) {
previewImg.src = e.target.result;
preview.style.display = 'block';
}
reader.readAsDataURL(file);
} else {
preview.style.display = 'none';
}
}
document.getElementById('broadcastForm').addEventListener('submit', async (e) => {
e.preventDefault();
const text = document.getElementById('broadcastText').value;
const photo = document.getElementById('broadcastPhoto').files[0];
const recipients = document.getElementById('broadcastRecipients').value;
const resultDiv = document.getElementById('result');
if (!text.trim()) {
alert('Введите текст объявления!');
return;
}
const recipientNames = {
'all_verified': 'всем верифицированным',
'ig_only': 'Инициативной Группе',
'chat_only': 'в общий чат',
'all_and_chat': 'всем верифицированным + в чат'
};
if (!confirm(`Отправить рассылку ${recipientNames[recipients] || 'всем'}?`)) return;
resultDiv.innerHTML = '<div class="alert alert-info">📤 Отправка рассылки...</div>';
const formData = new FormData();
formData.append('text', text);
formData.append('recipients', recipients);
if (photo) {
formData.append('photo', photo);
}
try {
const r = await fetch('/api/broadcast/send', {
method: 'POST',
body: formData
});
const result = await r.json();
if (result.success) {
resultDiv.innerHTML = `<div class="alert alert-success">✅ ${result.message}</div>`;
document.getElementById('broadcastForm').reset();
document.getElementById('preview').style.display = 'none';
} else {
resultDiv.innerHTML = `<div class="alert alert-danger">❌ Ошибка: ${result.error || 'Неизвестная'}</div>`;
}
} catch (err) {
resultDiv.innerHTML = `<div class="alert alert-danger">❌ Ошибка: ${err.message}</div>`;
}
});
</script>
</body>
</html>