Compare commits
No commits in common. "fca335423efa7f085ab89ae305d3eec35fc4e8cf" and "6d530c9d9730b0716059a5c6de1bde54eace8072" have entirely different histories.
fca335423e
...
6d530c9d97
5 changed files with 58 additions and 79 deletions
|
|
@ -130,7 +130,6 @@ async def cmd_legal_sync(message: Message):
|
|||
|
||||
|
||||
@router.message(Command('admin'))
|
||||
@router.message(F.text.in_(['⚙️ Управление', '⚙️ Админка', '⚙️ АДМИНКА']))
|
||||
@admin_only
|
||||
async def cmd_admin(message: Message):
|
||||
"""Админ-меню (только в личке!)"""
|
||||
|
|
@ -151,7 +150,7 @@ async def cmd_admin(message: Message):
|
|||
)
|
||||
|
||||
|
||||
@router.message(F.text.in_(['📊 Статистика', '/stats']))
|
||||
@router.message(F.text == '📊 Статистика')
|
||||
@admin_only
|
||||
async def msg_stats_button(message: Message):
|
||||
"""Кнопка Статистика"""
|
||||
|
|
@ -163,19 +162,19 @@ async def msg_stats_button(message: Message):
|
|||
await message.answer(text, parse_mode='HTML')
|
||||
|
||||
|
||||
@router.message(F.text.in_(['🛡️ Фильтр посторонних', '🕵️ Шпионы', '/spies']))
|
||||
@router.message(F.text == '🕵️ Шпионы')
|
||||
@admin_only
|
||||
async def msg_spies_button(message: Message):
|
||||
"""Кнопка Шпионы / Фильтр посторонних"""
|
||||
"""Кнопка Шпионы"""
|
||||
async with AsyncSessionLocal() as session:
|
||||
spy_detector = SpyDetector(session)
|
||||
spies = await spy_detector.get_spies()
|
||||
|
||||
if not spies:
|
||||
await message.answer('🟢 Посторонних и шпионов не обнаружено')
|
||||
await message.answer('🟢 Шпионов не обнаружено')
|
||||
return
|
||||
|
||||
text = '🚨 <b>ПОСТОРОННИЕ / ШПИОНЫ</b> 🚨\n\n'
|
||||
text = '🚨 <b>ШПИОНЫ</b> 🚨\n\n'
|
||||
for spy in spies:
|
||||
name = spy.full_name or f'user{spy.user_id}'
|
||||
apt = spy.apartment or '?'
|
||||
|
|
@ -184,16 +183,16 @@ async def msg_spies_button(message: Message):
|
|||
await message.answer(text, parse_mode='HTML')
|
||||
|
||||
|
||||
@router.message(F.text.in_(['👥 Список жителей', '👥 Пользователи', '/users']))
|
||||
@router.message(F.text == '👥 Пользователи')
|
||||
@admin_only
|
||||
async def msg_users_button(message: Message):
|
||||
"""Кнопка Пользователи / Список жителей"""
|
||||
"""Кнопка Пользователи"""
|
||||
async with AsyncSessionLocal() as session:
|
||||
stmt = select(User).limit(50)
|
||||
result = await session.execute(stmt)
|
||||
users = list(result.scalars().all())
|
||||
|
||||
text = f'👥 <b>Список жителей</b> (50):\n\n'
|
||||
text = f'👥 <b>Пользователи</b> (50):\n\n'
|
||||
for user in users:
|
||||
status = '✅' if user.verified else '⏳'
|
||||
name = user.full_name or f'user{user.user_id}'
|
||||
|
|
@ -205,7 +204,7 @@ async def msg_users_button(message: Message):
|
|||
await message.answer(text, parse_mode='HTML')
|
||||
|
||||
|
||||
@router.message(F.text.in_(['❓ Справка', '❓ Помощь', '/help']), lambda m: config.is_admin(m.from_user.id))
|
||||
@router.message(F.text == '❓ Помощь')
|
||||
@admin_only
|
||||
async def msg_help_button(message: Message):
|
||||
"""Кнопка Помощь - справка по командам"""
|
||||
|
|
@ -700,7 +699,6 @@ async def cmd_spies(message: Message):
|
|||
|
||||
@router.message(Command('export'))
|
||||
@router.message(Command('экспорт'))
|
||||
@router.message(F.text.in_(['📥 Выгрузка базы', '📥 Экспорт JSON']))
|
||||
@admin_only
|
||||
async def cmd_export(message: Message):
|
||||
"""Экспорт данных"""
|
||||
|
|
|
|||
|
|
@ -110,7 +110,6 @@ async def cb_main_menu(callback: CallbackQuery):
|
|||
)
|
||||
|
||||
@router.message(Command('phones'))
|
||||
@router.message(F.text.in_(['📞 Важные телефоны', '📞 Телефоны', 'Телефоны']))
|
||||
@router.callback_query(F.data == 'phones')
|
||||
async def cb_phones(event):
|
||||
text = '📞 <b>ЭКСТРЕННЫЕ ТЕЛЕФОНЫ</b>\n\nВыберите категорию ниже:'
|
||||
|
|
@ -118,7 +117,7 @@ async def cb_phones(event):
|
|||
await safe_edit_or_reply(event, text, reply_markup=kb, parse_mode='HTML')
|
||||
|
||||
@router.message(Command('masters'))
|
||||
@router.message(F.text.in_(['👷 Мастера', 'Мастера', 'мастера']))
|
||||
@router.message(F.text.lower() == 'мастера')
|
||||
async def cmd_masters(message: Message):
|
||||
"""Выдача списка мастеров по команде /masters или тексту 'мастера'"""
|
||||
from database.models import Service
|
||||
|
|
@ -335,7 +334,6 @@ class HelpFeedbackState(StatesGroup):
|
|||
waiting_for_reply = State()
|
||||
|
||||
@router.message(Command('help'))
|
||||
@router.message(F.text.in_(['❓ Справка', '❓ Помощь', 'Справка', 'Помощь']))
|
||||
@router.callback_query(F.data == 'help')
|
||||
async def cb_help(event, state: FSMContext):
|
||||
await state.clear()
|
||||
|
|
|
|||
|
|
@ -14,52 +14,53 @@ def get_main_menu(verified: bool = False, is_ig: bool = False, is_admin: bool =
|
|||
|
||||
# СТРОКА 1: БАЗА (ВСЕГДА ТУТ)
|
||||
builder.row(
|
||||
InlineKeyboardButton(text='📞 Важные телефоны', callback_data='phones'),
|
||||
InlineKeyboardButton(text='📞 Телефоны', callback_data='phones'),
|
||||
InlineKeyboardButton(text='⚖️ Юрист (ЖК РФ)', callback_data='main_jurist')
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(text='🏢 Паспорт дома', callback_data='about_house')
|
||||
InlineKeyboardButton(text='🏢 О доме', callback_data='about_house')
|
||||
)
|
||||
|
||||
# СТРОКА 2-3: ДЛЯ ВЕРИФИЦИРОВАННЫХ (ФИКСИРОВАННОЕ МЕСТО)
|
||||
if verified:
|
||||
builder.row(
|
||||
InlineKeyboardButton(text='⚠️ Отключения', callback_data='schedule'),
|
||||
InlineKeyboardButton(text='💳 Оплата ЖКХ', callback_data='payments')
|
||||
InlineKeyboardButton(text='📅 График', callback_data='schedule'),
|
||||
InlineKeyboardButton(text='💰 Платежи', callback_data='payments')
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(text='🗳️ Голосования', callback_data='polls'),
|
||||
InlineKeyboardButton(text='🗓️ События дома', callback_data='events')
|
||||
InlineKeyboardButton(text='📋 Опросы', callback_data='polls'),
|
||||
InlineKeyboardButton(text='🎉 События', callback_data='events')
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(text='📢 Объявления', callback_data='market'),
|
||||
InlineKeyboardButton(text='🤝 Добрые соседи', callback_data='achievements')
|
||||
InlineKeyboardButton(text='🏪 Маркет', callback_data='market'),
|
||||
InlineKeyboardButton(text='🏆 Рейтинг', callback_data='achievements')
|
||||
)
|
||||
else:
|
||||
# Для гостей кнопка верификации на видном месте
|
||||
builder.row(
|
||||
InlineKeyboardButton(text='🔑 Подтвердить квартиру', callback_data='start_verification')
|
||||
InlineKeyboardButton(text='🛡️ ВЕРИФИКАЦИЯ', callback_data='start_verification')
|
||||
)
|
||||
|
||||
# СТРОКА 4: ИНИЦИАТИВНАЯ ГРУППА И АДМИН
|
||||
if is_ig or is_admin:
|
||||
buttons = []
|
||||
if is_ig:
|
||||
buttons.append(InlineKeyboardButton(text='👥 Совет Дома', callback_data='ig_menu'))
|
||||
buttons.append(InlineKeyboardButton(text='🎯 ТИМ-МЕНЮ', callback_data='ig_menu'))
|
||||
if is_admin:
|
||||
buttons.append(InlineKeyboardButton(text='⚙️ Управление', callback_data='admin_menu'))
|
||||
buttons.append(InlineKeyboardButton(text='⚙️ АДМИНКА', callback_data='admin_menu'))
|
||||
builder.row(*buttons)
|
||||
|
||||
# СТРОКА 5: СУПЕР-АДМИН (ПРЕДСЕДАТЕЛЬ)
|
||||
# СТРОКА 5: СУПЕР-АДМИН (ТЫ)
|
||||
if is_superadmin:
|
||||
builder.row(
|
||||
InlineKeyboardButton(text='🛡️ Панель председателя', callback_data='superadmin_menu')
|
||||
InlineKeyboardButton(text='👑 [ROOT: УПРАВЛЕНИЕ]', callback_data='superadmin_menu')
|
||||
)
|
||||
|
||||
# СТРОКА 6: СЛУЖЕБНОЕ (НИЗ)
|
||||
builder.row(
|
||||
InlineKeyboardButton(text='❓ Справка', callback_data='help'),
|
||||
InlineKeyboardButton(text='👤 Моя квартира', callback_data='my_profile')
|
||||
InlineKeyboardButton(text='❓ Помощь', callback_data='help'),
|
||||
InlineKeyboardButton(text='🌦️ Погода', callback_data='weather'),
|
||||
InlineKeyboardButton(text='👤 Профиль', callback_data='my_profile')
|
||||
)
|
||||
|
||||
return builder.as_markup()
|
||||
|
|
@ -123,14 +124,14 @@ def get_admin_menu() -> InlineKeyboardMarkup:
|
|||
|
||||
builder.row(
|
||||
InlineKeyboardButton(text='📊 Статистика', callback_data='admin_stats'),
|
||||
InlineKeyboardButton(text='🛡️ Фильтр посторонних', callback_data='admin_spies'),
|
||||
InlineKeyboardButton(text='🕵️ Шпионы', callback_data='admin_spies'),
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(text='👥 Список жителей', callback_data='admin_users'),
|
||||
InlineKeyboardButton(text='⏳ Ожидают проверки', callback_data='admin_unverified'),
|
||||
InlineKeyboardButton(text='👥 Пользователи', callback_data='admin_users'),
|
||||
InlineKeyboardButton(text='⏳ Непроверенные', callback_data='admin_unverified'),
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(text='📥 Выгрузка базы', callback_data='admin_export'),
|
||||
InlineKeyboardButton(text='📥 Экспорт JSON', callback_data='admin_export'),
|
||||
InlineKeyboardButton(text='📤 Рассылка', callback_data='admin_broadcast'),
|
||||
)
|
||||
builder.row(
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@ def get_reply_menu() -> ReplyKeyboardMarkup:
|
|||
builder = ReplyKeyboardBuilder()
|
||||
|
||||
builder.row(
|
||||
KeyboardButton(text='📞 Важные телефоны'),
|
||||
KeyboardButton(text='📞 Телефоны'),
|
||||
KeyboardButton(text='👷 Мастера'),
|
||||
)
|
||||
builder.row(
|
||||
KeyboardButton(text='❓ Справка'),
|
||||
KeyboardButton(text='❓ Помощь'),
|
||||
)
|
||||
|
||||
# Resize keyboard чтобы не занимал много места
|
||||
|
|
@ -29,13 +29,12 @@ def get_admin_reply_menu() -> ReplyKeyboardMarkup:
|
|||
builder = ReplyKeyboardBuilder()
|
||||
|
||||
builder.row(
|
||||
KeyboardButton(text='⚙️ Управление'),
|
||||
KeyboardButton(text='📊 Статистика'),
|
||||
KeyboardButton(text='/admin'),
|
||||
KeyboardButton(text='/stats'),
|
||||
)
|
||||
builder.row(
|
||||
KeyboardButton(text='🛡️ Фильтр посторонних'),
|
||||
KeyboardButton(text='📥 Выгрузка базы'),
|
||||
KeyboardButton(text='/spies'),
|
||||
KeyboardButton(text='/export'),
|
||||
)
|
||||
|
||||
return builder.as_markup(resize_keyboard=True)
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class Scheduler:
|
|||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.scheduler = AsyncIOScheduler(timezone='Europe/Ulyanovsk')
|
||||
self.scheduler = AsyncIOScheduler()
|
||||
|
||||
def start(self):
|
||||
"""Запуск планировщика"""
|
||||
|
|
@ -141,24 +141,25 @@ class Scheduler:
|
|||
)
|
||||
logger.info('✅ Добавлена задача ежедневного прогноза погоды для админа')
|
||||
|
||||
# [ОТКЛЮЧЕНО ПО ТЗ] Еженедельный прогноз в чат по понедельникам и четвергам отключен для группы дома
|
||||
# Жильцы начали активнее общаться, прогноз погоды в общей группе больше не требуется.
|
||||
# Личный прогноз для Александра (daily_admin_weather) выше остается активным.
|
||||
# self.scheduler.add_job(
|
||||
# self.monday_chat_weather,
|
||||
# CronTrigger.from_crontab('0 9 * * 1'),
|
||||
# id='monday_chat_weather',
|
||||
# name='Прогноз погоды в чат (Понедельник)',
|
||||
# replace_existing=True
|
||||
# )
|
||||
# self.scheduler.add_job(
|
||||
# self.thursday_chat_weather,
|
||||
# CronTrigger.from_crontab('0 9 * * 4'),
|
||||
# id='thursday_chat_weather',
|
||||
# name='Прогноз погоды в чат (Четверг)',
|
||||
# replace_existing=True
|
||||
# )
|
||||
logger.info('ℹ️ Прогноз погоды в общий чат дома отключен по требованию председателя')
|
||||
# Еженедельный прогноз в чат по понедельникам (в 09:00)
|
||||
self.scheduler.add_job(
|
||||
self.monday_chat_weather,
|
||||
CronTrigger.from_crontab('0 9 * * 1'), # Пн в 09:00
|
||||
id='monday_chat_weather',
|
||||
name='Прогноз погоды в чат (Понедельник)',
|
||||
replace_existing=True
|
||||
)
|
||||
logger.info('✅ Добавлена задача прогноза погоды в чат по понедельникам')
|
||||
|
||||
# Еженедельный прогноз в чат по четвергам (в 09:00)
|
||||
self.scheduler.add_job(
|
||||
self.thursday_chat_weather,
|
||||
CronTrigger.from_crontab('0 9 * * 4'), # Чт в 09:00
|
||||
id='thursday_chat_weather',
|
||||
name='Прогноз погоды в чат (Четверг)',
|
||||
replace_existing=True
|
||||
)
|
||||
logger.info('✅ Добавлена задача прогноза погоды в чат по четвергам')
|
||||
|
||||
# Еженедельный дайджест Александра (суббота в 08:00)
|
||||
self.scheduler.add_job(
|
||||
|
|
@ -549,17 +550,8 @@ class Scheduler:
|
|||
"""Прогноз в чат по понедельникам (на 3 дня)"""
|
||||
try:
|
||||
from services.weather import WeatherService
|
||||
from config import ADMIN_CHAT_ID, ADMIN_USER_ID
|
||||
from config import ADMIN_CHAT_ID
|
||||
forecast = await WeatherService.get_3day_forecast()
|
||||
if not forecast or forecast.startswith("❌"):
|
||||
logger.warning(f"Monday weather forecast failed: {forecast}")
|
||||
await self.bot.send_message(
|
||||
ADMIN_USER_ID,
|
||||
f"⚠️ <b>Внимание:</b> Не удалось сгенерировать прогноз погоды на понедельник для общего чата:\n{forecast}",
|
||||
parse_mode='HTML'
|
||||
)
|
||||
return
|
||||
|
||||
await self.bot.send_message(
|
||||
ADMIN_CHAT_ID,
|
||||
forecast,
|
||||
|
|
@ -573,17 +565,8 @@ class Scheduler:
|
|||
"""Прогноз в чат по четвергам (на остаток недели и выходные)"""
|
||||
try:
|
||||
from services.weather import WeatherService
|
||||
from config import ADMIN_CHAT_ID, ADMIN_USER_ID
|
||||
from config import ADMIN_CHAT_ID
|
||||
forecast = await WeatherService.get_thursday_weekend_broadcast()
|
||||
if not forecast or forecast.startswith("❌"):
|
||||
logger.warning(f"Thursday weather forecast failed: {forecast}")
|
||||
await self.bot.send_message(
|
||||
ADMIN_USER_ID,
|
||||
f"⚠️ <b>Внимание:</b> Не удалось сгенерировать прогноз погоды на четверг для общего чата:\n{forecast}",
|
||||
parse_mode='HTML'
|
||||
)
|
||||
return
|
||||
|
||||
await self.bot.send_message(
|
||||
ADMIN_CHAT_ID,
|
||||
forecast,
|
||||
|
|
|
|||
Loading…
Reference in a new issue