diff --git a/data/broadcast_Gemini_Generated_Image_7v4paw7v4paw7v4p.png b/data/broadcast_Gemini_Generated_Image_7v4paw7v4paw7v4p.png new file mode 100644 index 0000000..21854d1 Binary files /dev/null and b/data/broadcast_Gemini_Generated_Image_7v4paw7v4paw7v4p.png differ diff --git a/data/service_images/masters_0d95ea9537e4480b82da5b475c34dfe2.png b/data/service_images/masters_0d95ea9537e4480b82da5b475c34dfe2.png new file mode 100644 index 0000000..b4bf3a6 Binary files /dev/null and b/data/service_images/masters_0d95ea9537e4480b82da5b475c34dfe2.png differ diff --git a/data/service_images/masters_715a0073539c410daae24f5b59c07b74.png b/data/service_images/masters_715a0073539c410daae24f5b59c07b74.png new file mode 100644 index 0000000..b4bf3a6 Binary files /dev/null and b/data/service_images/masters_715a0073539c410daae24f5b59c07b74.png differ diff --git a/handlers/users.py b/handlers/users.py index 6206fad..2ac8cc9 100644 --- a/handlers/users.py +++ b/handlers/users.py @@ -206,8 +206,7 @@ async def cmd_help(message: Message): 📱 Доступные команды: Для всех: -📞 /телефон — Экстренные службы -👷 /мастера — Сантехники, электрики +📞 /телефон — Экстренные службы (включая мастеров) 👮 /участковый — Контакты полиции 📜 /правила — Правила чата ❓ /помощь — Эта справка diff --git a/keyboards/inline.py b/keyboards/inline.py index de74e59..b352cb1 100644 --- a/keyboards/inline.py +++ b/keyboards/inline.py @@ -39,10 +39,7 @@ def get_main_menu(verified: bool = False, is_ig: bool = False, is_admin: bool = builder.row( InlineKeyboardButton(text=f'{level_emoji} 📞 Экстренные телефоны', callback_data='phones'), ) - builder.row( - InlineKeyboardButton(text=f'{level_emoji} 👷 Мастера', callback_data='masters'), - ) - + # Только для верифицированных if verified: builder.row( @@ -207,7 +204,6 @@ def get_unverified_menu() -> InlineKeyboardMarkup: InlineKeyboardButton(text=f'{emoji} 📞 Экстренные телефоны', callback_data='phones'), ) builder.row( - InlineKeyboardButton(text=f'{emoji} 👷 Мастера', callback_data='masters'), InlineKeyboardButton(text=f'{emoji} 👮 Участковый', callback_data='police'), ) builder.row( diff --git a/keyboards/reply.py b/keyboards/reply.py index 59d24d4..6518252 100644 --- a/keyboards/reply.py +++ b/keyboards/reply.py @@ -11,15 +11,14 @@ def get_reply_menu() -> ReplyKeyboardMarkup: Используется редко, в основном для важных действий """ builder = ReplyKeyboardBuilder() - + builder.row( KeyboardButton(text='📞 Телефоны'), - KeyboardButton(text='👷 Мастера'), ) builder.row( KeyboardButton(text='❓ Помощь'), ) - + # Resize keyboard чтобы не занимал много места return builder.as_markup(resize_keyboard=True, one_time_keyboard=False) diff --git a/send_forum_capabilities.py b/send_forum_capabilities.py new file mode 100644 index 0000000..8475b37 --- /dev/null +++ b/send_forum_capabilities.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 +""" +Отправка подробного отчёта о возможностях рассылки бота +""" +import asyncio +import sys +from pathlib import Path + +BASE_DIR = Path(__file__).resolve().parent +sys.path.insert(0, str(BASE_DIR)) + +from aiogram import Bot +from aiogram.client.default import DefaultBotProperties +from aiogram.enums import ParseMode +from aiogram.client.session.aiohttp import AiohttpSession + +from config import BOT_TOKEN, ADMIN_USER_ID, get_proxy_url + +async def send_report(): + """Отправка отчёта админу""" + proxy_url = get_proxy_url() + + if proxy_url: + session = AiohttpSession(proxy=proxy_url) + bot = Bot(token=BOT_TOKEN, session=session, default=DefaultBotProperties(parse_mode=ParseMode.HTML)) + else: + bot = Bot(token=BOT_TOKEN, default=DefaultBotProperties(parse_mode=ParseMode.HTML)) + + message = """ +📊 АНАЛИЗ ВОЗМОЖНОСТЕЙ РАССЫЛКИ ДОМОВОГО БОТА + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +🎯 ОТВЕТЫ НА ТВОИ ВОПРОСЫ: + +✅ 1. МОЖЕТ ЛИ БОТ ПИСАТЬ В ОТДЕЛЬНУЮ ТЕМУ? + +Да! Бот уже настроен для работы с темами форума Telegram. + +📋 Настроенные темы: +├ 1️⃣ Общий чат (ID: 1) +├ 2️⃣ Важные объявления (ID: 6258) +├ 3️⃣ Ссылки (ID: 6265) +├ 4️⃣ Мемы (ID: 6262) +├ 5️⃣ Для автовладельцев (ID: 6266) +├ 6️⃣ Потеряшки (ID: 6263) +├ 7️⃣ Собрания собственников (ID: 6261) +├ 8️⃣ Вывоз КГМ (ID: 6264) +├ 9️⃣ Переписка с УО (ID: 6260) +└ 🔟 Важные телефоны (ID: 6259) + +💡 Как работает: +Бот использует параметр message_thread_id при отправке: +• send_message(chat_id=CHAT_ID, message_thread_id=TOPIC_ID) +• Это позволяет писать в КОНКРЕТНУЮ тему! + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +✅ 2. МОЖЕТ ЛИ БОТ ПИСАТЬ В ОБЩИЙ ЧАТ? + +Да! Уже реализовано в нескольких местах: + +📤 Способы рассылки: + +1️⃣ Через админку в Telegram: + /admin → кнопка «📤 Рассылка» + → Отправляет в ОБЩУЮ ТЕМУ + всем жильцам в личку + +2️⃣ Через веб-панель (порт 8000): + http://localhost:8000/broadcast + → Выбор получателей: + • Все верифицированные + чат + • Только ИГ + • Только чат + → Отправка в ОБЩУЮ ТЕМУ чата + +3️⃣ Через команды: + • /ig_broadcast — рассылка только ИГ + • Рассылка админа — всем жильцам + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +🎭 ДЛЯ ТВОЕЙ ЗАДАЧИ (ЮМОР ПРО МУСОР + ДОНАТ): + +Вариант 1: Общий чат +• Писать в тему «Общий чат» (ID: 1) +• Увидят ВСЕ жильцы +• Подходит для доната на суд + +Вариант 2: Тема Мемы +• Писать в тему «Мемы» (ID: 6262) +• Идеально для юмора про мусор! + +Вариант 3: Тема Объявления +• Писать в тему «Важные объявления» (ID: 6258) +• Для серьёзных постов про донат + +Вариант 4: Тема Собрания +• Писать в тему «Собрания собственников» (ID: 6261) +• Для важных решений по УК + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +🔧 КАК ЭТО БУДЕТ РАБОТАТЬ: + +Создам скрипт, который: +✅ Пишет пост в КОНКРЕТНУЮ тему +✅ Использует прокси (VPN Германия) +✅ Форматирует текст с emoji +✅ Может добавить картинку +✅ Постит серию постов с интервалом + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +🚀 ГОТОВО К РАБОТЕ! + +Бот уже имеет ВСЕ необходимые функции: +✅ Отправка в конкретную тему +✅ Отправка в общий чат +✅ Рассылка всем жильцам +✅ Поддержка фото +✅ Работа через прокси + +Нужно только создать скрипт для постинга серии сообщений! + """ + + try: + await bot.send_message(ADMIN_USER_ID, message) + print("✅ Подробный отчёт отправлен!") + except Exception as e: + print(f"❌ Ошибка: {e}") + finally: + await bot.session.close() + +if __name__ == '__main__': + asyncio.run(send_report()) diff --git a/send_scheduler_analysis.py b/send_scheduler_analysis.py new file mode 100644 index 0000000..0f588c6 --- /dev/null +++ b/send_scheduler_analysis.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python3 +""" +Отправка анализа возможности планировщика постов +""" +import asyncio +import sys +from pathlib import Path + +BASE_DIR = Path(__file__).resolve().parent +sys.path.insert(0, str(BASE_DIR)) + +from aiogram import Bot +from aiogram.client.default import DefaultBotProperties +from aiogram.enums import ParseMode +from aiogram.client.session.aiohttp import AiohttpSession + +from config import BOT_TOKEN, ADMIN_USER_ID, get_proxy_url + +async def send_analysis(): + """Отправка анализа планировщика постов""" + proxy_url = get_proxy_url() + + if proxy_url: + session = AiohttpSession(proxy=proxy_url) + bot = Bot(token=BOT_TOKEN, session=session, default=DefaultBotProperties(parse_mode=ParseMode.HTML)) + else: + bot = Bot(token=BOT_TOKEN, default=DefaultBotProperties(parse_mode=ParseMode.HTML)) + + message = """ +📅 АНАЛИЗ: ПЛАНИРОВЩИК ПОСТОВ ДЛЯ ВЕБ-ПАНЕЛИ + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +🎯 МОЖЕМ ЛИ РЕАЛИЗОВАТЬ? + +✅ ДА! Полностью реализуемо! + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +📋 ЧТО УЖЕ ЕСТЬ В КОДЕ: + +✅ 1. Планировщик (Scheduler) + • Файл: services/scheduler.py + • Использует APScheduler + • Уже работает с cron-задачами + • Запускается при старте бота + +✅ 2. Веб-панель (FastAPI) + • Файл: web/app.py (911 строк) + • Есть страница /broadcast + • Уже умеет: текст + фото + • Уже есть выбор получателей + +✅ 3. База данных (SQLAlchemy) + • Есть модели для всего + • Нужно добавить: ScheduledPost + • Хранить: текст, фото, тему, время + +✅ 4. Темы форума + • Настроены 10 тем + • message_thread_id работает + • config.get_topic_id('topic_name') + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +🔧 ЧТО НУЖНО ДОБАВИТЬ: + +1️⃣ Новая модель в БД: + ScheduledPost + • id + • text (текст поста) + • photo_file_id (опционально) + • topic_id (тема: 1, 6258, 6262...) + • topic_name ('general', 'memes'...) + • scheduled_time (когда отправить) + • status ('pending', 'sent', 'failed') + • created_at (когда создан) + • error_message (если ошибка) + +2️⃣ Новая страница в веб-панели: + /scheduled_posts + • Текстовое поле (с HTML поддержкой) + • Загрузка фото + • Выпадающий список тем + • Датапикер (календарь) + • Время (часы:минуты) + • Кнопка "Запланировать" + • Таблица запланированных + • Кнопки: удалить, редактировать + +3️⃣ API endpoints: + • GET /scheduled_posts - страница + • POST /api/scheduled_posts/create + • POST /api/scheduled_posts/cancel + • GET /api/scheduled_posts/list + • DELETE /api/scheduled_posts/{id} + +4️⃣ Фоновая задача в Scheduler: + • Проверка каждую минуту + • SELECT * FROM scheduled_posts + WHERE scheduled_time <= NOW() + AND status = 'pending' + • Отправка в нужную тему + • Обновление status = 'sent' + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +🎨 КАК БУДЕТ ВЫГЛЯДЕТЬ: + +Форма создания поста: + +📝 Текст: [большое поле] +📷 Фото: [выбрать файл] +📂 Тема: [выпадающий список ▼] + • Общий чат + • Мемы + • Объявления + • Собрания + • ... +📅 Дата: [🗓️ 2026-04-07] +⏰ Время: [14:30] + +[✅ Запланировать пост] + +Таблица запланированных: + +| # | Тема | Когда | Статус | Действия | +|---|------|-------|--------|----------| +| 1 | Мемы | 07.04 10:00 | ⏳ | ❌ ✏️ | +| 2 | Общий | 08.04 15:00 | ⏳ | ❌ ✏️ | +| 3 | КГМ | 09.04 09:00 | ✅ | - | + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +🛡️ БЕЗОПАСНОСТЬ (НЕ ИСПОРТИМ ТЕКУЩЕЕ): + +✅ Изолированные изменения: + • Новая модель - отдельная таблица + • Новый endpoint - новый URL + • Новая страница - новый файл HTML + • Новая задача в scheduler - отдельный job + +✅ Миграция БД: + • ALTER TABLE (добавление таблицы) + • Не меняем существующие таблицы + • Без риска для текущих данных + +✅ Тестирование: + • Можно тестировать на одном посте + • Отмена без последствий + • Логи всех действий + +✅ Откат: + • Если что-то не так - удаляем таблицу + • Новый код не трогает старый + • Старый функционал не изменится + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +🚀 ПЛАН РЕАЛИЗАЦИИ: + +Шаг 1: Добавить модель ScheduledPost в БД +Шаг 2: Создать страницу /scheduled_posts +Шаг 3: Создать API endpoints +Шаг 4: Добавить задачу в Scheduler +Шаг 5: Тестирование на 1-2 постах +Шаг 6: Коммит в Gitea + +⚠️ ВАЖНО: +Ни один существующий файл НЕ будет изменён критично! +Только добавления нового кода. + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +💡 СЦЕНАРИЙ ИСПОЛЬЗОВАНИЯ: + +1. Заходишь в веб-панель (порт 8000) +2. Кликаешь "📅 Запланированные посты" +3. Заполняешь форму: + • Текст про юмор/мусор + • Прикладываешь картинку + • Выбираешь тему "Мемы" + • Ставишь: 07.04.2026 10:00 +4. Жмёшь "Запланировать" +5. Повторяешь для 5-6 постов +6. Бот сам отправит их по расписанию! + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +✅ ГОТОВО К РЕАЛИЗАЦИИ! + +Можем начинать? Никакой текущий функционал не пострадает! + """ + + try: + await bot.send_message(ADMIN_USER_ID, message) + print("✅ Анализ планировщика отправлен!") + except Exception as e: + print(f"❌ Ошибка: {e}") + finally: + await bot.session.close() + +if __name__ == '__main__': + asyncio.run(send_analysis()) diff --git a/send_status_notification.py b/send_status_notification.py new file mode 100644 index 0000000..3f7f57d --- /dev/null +++ b/send_status_notification.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +""" +Отправка уведомления через Домовой Бот +""" +import asyncio +import sys +from pathlib import Path + +# Добавляем путь к проекту +BASE_DIR = Path(__file__).resolve().parent +sys.path.insert(0, str(BASE_DIR)) + +from aiogram import Bot +from aiogram.client.default import DefaultBotProperties +from aiogram.enums import ParseMode +from aiogram.client.session.aiohttp import AiohttpSession + +from config import BOT_TOKEN, ADMIN_USER_ID, get_proxy_url + +async def send_notification(): + """Отправка уведомления админу""" + proxy_url = get_proxy_url() + + if proxy_url: + session = AiohttpSession(proxy=proxy_url) + bot = Bot(token=BOT_TOKEN, session=session, default=DefaultBotProperties(parse_mode=ParseMode.HTML)) + else: + bot = Bot(token=BOT_TOKEN, default=DefaultBotProperties(parse_mode=ParseMode.HTML)) + + message = """ +✅ Домовой Бот — Статус системы + +🟢 VPN (Xray): РАБОТАЕТ +├ Сервер: 🇩🇪 Германия (138.124.228.10) +├ Протокол: VLESS + Reality +├ SOCKS5: 127.0.0.1:10808 ✅ +└ HTTP: 127.0.0.1:10809 ✅ + +🤖 Бот: ГОТОВ К РАБОТЕ +├ Версия: v2.1 +├ Прокси: подключён +└ Веб-интерфейс: порт 8000 + +📋 Доступные функции: +• 📊 Управление жильцами +• 📢 Доска объявлений +• 📅 Календарь событий +• 📝 Опросы и голосования +• 🏆 Рейтинг и достижения +• 🔔 Напоминания о платежах +• 🛡️ Анти-спам система +• 📱 Веб-админка + +🎯 Готов к публикации постов! + +Система работает стабильно ✓ + """ + + try: + await bot.send_message(ADMIN_USER_ID, message) + print("✅ Уведомление отправлено!") + except Exception as e: + print(f"❌ Ошибка: {e}") + finally: + await session.close() if proxy_url else await bot.session.close() + +if __name__ == '__main__': + asyncio.run(send_notification()) diff --git a/web/app.py b/web/app.py index 348ccc0..0594ac7 100644 --- a/web/app.py +++ b/web/app.py @@ -10,6 +10,7 @@ from fastapi.security import HTTPBasic, HTTPBasicCredentials from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse +from fastapi.middleware.cors import CORSMiddleware from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, func, update, delete, desc from datetime import datetime, timedelta @@ -18,13 +19,23 @@ import config from database.db import AsyncSessionLocal from database.models import ( - User, Message, Poll, Ad, PaymentReminder, Schedule, + User, Message, Poll, Ad, PaymentReminder, Schedule, VerificationRequest, Service, Announcement, Event ) logger = logging.getLogger(__name__) app = FastAPI(title="Домовой Бот - MEGA Admin Panel", version="3.0.0") + +# Добавляем CORS middleware +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], # Разрешаем все origins (для локального использования) + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + templates = Jinja2Templates(directory="web/templates") app.mount("/static", StaticFiles(directory="web/static"), name="static") app.mount("/static/service_images", StaticFiles(directory="data/service_images"), name="service_images") @@ -538,6 +549,9 @@ async def api_send_broadcast( from pathlib import Path from services.initiative_group import InitiativeGroupService + # Логируем начало рассылки + logger.info(f"📬 НАЧАЛО РАССЫЛКИ: получатели={recipients}, текст_len={len(text) if text else 0}") + photo_id = None # Если есть картинка — загружаем @@ -650,6 +664,9 @@ async def api_send_broadcast( except Exception as e: logger.error(f"Ошибка логирования рассылки: {e}") + # Логируем результат + logger.info(f"✅ РАССЫЛКА ЗАВЕРШЕНА: успешно={success_count}, ошибок={error_count}, получатели={recipients}") + return JSONResponse({ "success": True, "message": f"Отправлено: {success_count} (в чат: {'✅' if chat_sent else '❌'})",