🎯 Упрощение меню жильца и исправление Участкового
✅ Убраны лишние кнопки из меню жильца: - 📢 Объявления (админский функционал) - 📣 Подать объявление - 📊 Токсичность (админский функционал) - 🏠 Темы чата (админский функционал) ✅ Остались только нужные жильцам кнопки: - 📞 Экстренные телефоны - 👷 Мастера - 👮 Участковый (работает через Service БД!) - 📅 График отключений - 🏆 Достижения - 📅 События - 💰 Платежи - 📋 Опросы - 🔍 Моя квартира - 🙏 Поблагодарить - ❓ Помощь ✅ Участковый берёт данные из Service таблицы ✅ Все сообщения бота только в личку пользователю 📝 Веб-админка: Телефоны → Добавить службу → Полиция Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
parent
fc7484889c
commit
b9ac442a89
2 changed files with 299 additions and 30 deletions
|
|
@ -282,33 +282,6 @@ async def cb_schedule(callback: CallbackQuery):
|
||||||
await callback.message.answer(text, parse_mode='HTML')
|
await callback.message.answer(text, parse_mode='HTML')
|
||||||
|
|
||||||
|
|
||||||
@router.callback_query(F.data == 'announcements')
|
|
||||||
async def cb_announcements(callback: CallbackQuery):
|
|
||||||
"""Объявления"""
|
|
||||||
await callback.answer()
|
|
||||||
|
|
||||||
async with AsyncSessionLocal() as session:
|
|
||||||
stmt = (
|
|
||||||
select(Announcement)
|
|
||||||
.where(Announcement.is_active == True)
|
|
||||||
.order_by(Announcement.is_pinned.desc(), Announcement.created_at.desc())
|
|
||||||
.limit(10)
|
|
||||||
)
|
|
||||||
result = await session.execute(stmt)
|
|
||||||
announcements = list(result.scalars().all())
|
|
||||||
|
|
||||||
if not announcements:
|
|
||||||
await callback.message.answer('📭 Объявлений нет')
|
|
||||||
return
|
|
||||||
|
|
||||||
text = '<b>📢 Объявления</b>\n\n'
|
|
||||||
for ann in announcements:
|
|
||||||
pin = '📌 ' if ann.is_pinned else ''
|
|
||||||
text += f'{pin}<b>{ann.title}</b>\n{ann.text[:200]}...\n\n'
|
|
||||||
|
|
||||||
await callback.message.answer(text, parse_mode='HTML')
|
|
||||||
|
|
||||||
|
|
||||||
@router.callback_query(F.data == 'main_menu')
|
@router.callback_query(F.data == 'main_menu')
|
||||||
async def cb_main_menu(callback: CallbackQuery):
|
async def cb_main_menu(callback: CallbackQuery):
|
||||||
"""Главное меню"""
|
"""Главное меню"""
|
||||||
|
|
@ -356,3 +329,240 @@ async def msg_masters_text(message: Message):
|
||||||
async def msg_help_text(message: Message):
|
async def msg_help_text(message: Message):
|
||||||
"""Текстовая команда помощь"""
|
"""Текстовая команда помощь"""
|
||||||
await cmd_help(message)
|
await cmd_help(message)
|
||||||
|
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# ОБРАБОТЧИКИ НОВЫХ КНОПОК МЕНЮ
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
from keyboards.inline import get_thank_user_keyboard, get_thank_confirm_keyboard
|
||||||
|
from sqlalchemy import desc
|
||||||
|
|
||||||
|
|
||||||
|
# Хранилище страниц для благодарностей
|
||||||
|
_thank_pages: dict[int, dict] = {}
|
||||||
|
|
||||||
|
|
||||||
|
@router.callback_query(F.data == 'achievements')
|
||||||
|
async def cb_achievements_menu(callback: CallbackQuery):
|
||||||
|
"""Кнопка Достижения"""
|
||||||
|
await callback.answer()
|
||||||
|
# Перенаправляем на команду /achievements
|
||||||
|
from handlers.achievements import cmd_achievements
|
||||||
|
await cmd_achievements(callback.message)
|
||||||
|
|
||||||
|
|
||||||
|
@router.callback_query(F.data == 'events')
|
||||||
|
async def cb_events_menu(callback: CallbackQuery):
|
||||||
|
"""Кнопка События"""
|
||||||
|
await callback.answer()
|
||||||
|
# Перенаправляем на команду /events
|
||||||
|
from handlers.events import cmd_events
|
||||||
|
await cmd_events(callback.message)
|
||||||
|
|
||||||
|
|
||||||
|
@router.callback_query(F.data == 'payments')
|
||||||
|
async def cb_payments_menu(callback: CallbackQuery):
|
||||||
|
"""Кнопка Платежи"""
|
||||||
|
await callback.answer()
|
||||||
|
# Перенаправляем на команду /payments
|
||||||
|
from handlers.payments import cmd_payments
|
||||||
|
await cmd_payments(callback.message)
|
||||||
|
|
||||||
|
|
||||||
|
@router.callback_query(F.data == 'polls')
|
||||||
|
async def cb_polls_menu(callback: CallbackQuery):
|
||||||
|
"""Кнопка Опросы"""
|
||||||
|
await callback.answer()
|
||||||
|
# Перенаправляем на команду /polls
|
||||||
|
from handlers.polls import cmd_polls
|
||||||
|
await cmd_polls(callback.message)
|
||||||
|
|
||||||
|
|
||||||
|
@router.callback_query(F.data == 'thank_user')
|
||||||
|
async def cb_thank_user_menu(callback: CallbackQuery):
|
||||||
|
"""Кнопка Поблагодарить - выбор пользователя"""
|
||||||
|
await callback.answer()
|
||||||
|
|
||||||
|
async with AsyncSessionLocal() as session:
|
||||||
|
# Показываем активных пользователей (по количеству сообщений)
|
||||||
|
stmt = select(User).order_by(desc(User.message_count)).limit(100)
|
||||||
|
result = await session.execute(stmt)
|
||||||
|
users = list(result.scalars().all())
|
||||||
|
|
||||||
|
if not users:
|
||||||
|
await callback.message.answer('📭 Пользователей нет')
|
||||||
|
return
|
||||||
|
|
||||||
|
# Сохраняем список для сессии
|
||||||
|
_thank_pages[callback.from_user.id] = {
|
||||||
|
'users': users,
|
||||||
|
'page': 0
|
||||||
|
}
|
||||||
|
|
||||||
|
await callback.message.answer(
|
||||||
|
'🙏 <b>Поблагодарить пользователя</b>\n\n'
|
||||||
|
'Выберите пользователя которому хотите сказать спасибо:\n'
|
||||||
|
'(+10 к рейтингу)',
|
||||||
|
reply_markup=get_thank_user_keyboard(users, page=0),
|
||||||
|
parse_mode='HTML'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.callback_query(F.data.startswith('thank_page_'))
|
||||||
|
async def cb_thank_page(callback: CallbackQuery):
|
||||||
|
"""Переключение страницы в списке для благодарности"""
|
||||||
|
await callback.answer()
|
||||||
|
|
||||||
|
page = int(callback.data.split('_')[-1])
|
||||||
|
user_data = _thank_pages.get(callback.from_user.id)
|
||||||
|
|
||||||
|
if not user_data:
|
||||||
|
await callback.message.answer('❌ Сессия истекла. Выберите "Поблагодарить" заново')
|
||||||
|
return
|
||||||
|
|
||||||
|
users = user_data['users']
|
||||||
|
user_data['page'] = page
|
||||||
|
|
||||||
|
await callback.message.edit_reply_markup(
|
||||||
|
reply_markup=get_thank_user_keyboard(users, page=page)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.callback_query(F.data.startswith('thank_select_'))
|
||||||
|
async def cb_thank_select(callback: CallbackQuery):
|
||||||
|
"""Выбор конкретного пользователя для благодарности"""
|
||||||
|
await callback.answer()
|
||||||
|
|
||||||
|
user_id = int(callback.data.split('_')[-1])
|
||||||
|
|
||||||
|
async with AsyncSessionLocal() as session:
|
||||||
|
stmt = select(User).where(User.user_id == user_id)
|
||||||
|
result = await session.execute(stmt)
|
||||||
|
user = result.scalar_one_or_none()
|
||||||
|
|
||||||
|
if not user:
|
||||||
|
await callback.message.answer('❌ Пользователь не найден')
|
||||||
|
return
|
||||||
|
|
||||||
|
name = user.full_name or f'user{user_id}'
|
||||||
|
apt = f' (кв. {user.apartment})' if user.apartment else ''
|
||||||
|
|
||||||
|
await callback.message.answer(
|
||||||
|
f'🙏 <b>Поблагодарить пользователя?</b>\n\n'
|
||||||
|
f'{name}{apt}\n\n'
|
||||||
|
f'Пользователь получит +10 к рейтингу.',
|
||||||
|
reply_markup=get_thank_confirm_keyboard(user_id),
|
||||||
|
parse_mode='HTML'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.callback_query(F.data.startswith('thank_confirm_'))
|
||||||
|
async def cb_thank_confirm(callback: CallbackQuery):
|
||||||
|
"""Подтверждение благодарности"""
|
||||||
|
await callback.answer()
|
||||||
|
|
||||||
|
target_user_id = int(callback.data.split('_')[-1])
|
||||||
|
from_user_id = callback.from_user.id
|
||||||
|
|
||||||
|
# Проверяем что не благодарит сам себя
|
||||||
|
if target_user_id == from_user_id:
|
||||||
|
await callback.message.answer('❌ Нельзя поблагодарить самого себя!')
|
||||||
|
return
|
||||||
|
|
||||||
|
async with AsyncSessionLocal() as session:
|
||||||
|
# Находим того кого благодарят
|
||||||
|
stmt = select(User).where(User.user_id == target_user_id)
|
||||||
|
result = await session.execute(stmt)
|
||||||
|
target_user = result.scalar_one_or_none()
|
||||||
|
|
||||||
|
# Находим того кто благодарит
|
||||||
|
stmt = select(User).where(User.user_id == from_user_id)
|
||||||
|
result = await session.execute(stmt)
|
||||||
|
from_user = result.scalar_one_or_none()
|
||||||
|
|
||||||
|
if not target_user or not from_user:
|
||||||
|
await callback.message.answer('❌ Пользователь не найден')
|
||||||
|
return
|
||||||
|
|
||||||
|
# Проверяли ли уже сегодня
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from database.models import Thanks
|
||||||
|
|
||||||
|
today = datetime.utcnow().date()
|
||||||
|
stmt = select(Thanks).where(
|
||||||
|
Thanks.from_user_id == from_user_id,
|
||||||
|
Thanks.created_at >= datetime.combine(today, datetime.min.time())
|
||||||
|
)
|
||||||
|
result = await session.execute(stmt)
|
||||||
|
existing = result.scalar_one_or_none()
|
||||||
|
|
||||||
|
if existing:
|
||||||
|
await callback.message.answer('ℹ️ Вы уже благодарили кого-то сегодня. Можно раз в сутки.')
|
||||||
|
return
|
||||||
|
|
||||||
|
# Создаём благодарность
|
||||||
|
thanks = Thanks(
|
||||||
|
from_user_id=from_user_id,
|
||||||
|
to_user_id=target_user_id,
|
||||||
|
message='Благодарность через меню',
|
||||||
|
)
|
||||||
|
session.add(thanks)
|
||||||
|
|
||||||
|
# +10 к рейтингу
|
||||||
|
target_user.rating += 10
|
||||||
|
|
||||||
|
await session.commit()
|
||||||
|
|
||||||
|
name = target_user.full_name or f'user{target_user_id}'
|
||||||
|
|
||||||
|
await callback.message.answer(
|
||||||
|
f'✅ <b>Спасибо отправлено!</b>\n\n'
|
||||||
|
f'{name} получил +10 к рейтингу.\n'
|
||||||
|
f'Вы можете поблагодарить ещё раз через 24 часа.',
|
||||||
|
parse_mode='HTML'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.callback_query(F.data == 'announcements')
|
||||||
|
async def cb_announcements_menu(callback: CallbackQuery):
|
||||||
|
"""Кнопка Объявления"""
|
||||||
|
await callback.answer()
|
||||||
|
# Перенаправляем на команду /ads
|
||||||
|
from handlers.ads import cmd_ads
|
||||||
|
await cmd_ads(callback.message)
|
||||||
|
|
||||||
|
|
||||||
|
@router.callback_query(F.data == 'schedule')
|
||||||
|
async def cb_schedule_menu(callback: CallbackQuery):
|
||||||
|
"""Кнопка График отключений"""
|
||||||
|
await callback.answer()
|
||||||
|
# Перенаправляем на команду /график
|
||||||
|
from handlers.schedule import cmd_schedule
|
||||||
|
await cmd_schedule(callback.message)
|
||||||
|
|
||||||
|
|
||||||
|
@router.callback_query(F.data == 'my_apartment')
|
||||||
|
async def cb_my_apartment_menu(callback: CallbackQuery):
|
||||||
|
"""Кнопка Моя квартира"""
|
||||||
|
await callback.answer()
|
||||||
|
|
||||||
|
async with AsyncSessionLocal() as session:
|
||||||
|
stmt = select(User).where(User.user_id == callback.from_user.id)
|
||||||
|
result = await session.execute(stmt)
|
||||||
|
user = result.scalar_one_or_none()
|
||||||
|
|
||||||
|
if not user:
|
||||||
|
await callback.message.answer('❌ Вы не зарегистрированы')
|
||||||
|
return
|
||||||
|
|
||||||
|
apt = user.apartment or '❓ Не указана'
|
||||||
|
verified = '✅' if user.verified else '⏳'
|
||||||
|
|
||||||
|
text = f'🏠 <b>Ваша квартира</b>\n\n'
|
||||||
|
text += f'Номер: <code>{apt}</code>\n'
|
||||||
|
text += f'Статус: {verified} {"Верифицирован" if user.verified else "Не верифицирован"}\n'
|
||||||
|
text += f'Рейтинг: {user.rating}\n'
|
||||||
|
text += f'Уровень: {user.level}\n'
|
||||||
|
|
||||||
|
await callback.message.answer(text, parse_mode='HTML')
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||||||
def get_main_menu() -> InlineKeyboardMarkup:
|
def get_main_menu() -> InlineKeyboardMarkup:
|
||||||
"""Главное меню жильца"""
|
"""Главное меню жильца"""
|
||||||
builder = InlineKeyboardBuilder()
|
builder = InlineKeyboardBuilder()
|
||||||
|
|
||||||
builder.row(
|
builder.row(
|
||||||
InlineKeyboardButton(text='📞 Экстренные телефоны', callback_data='phones'),
|
InlineKeyboardButton(text='📞 Экстренные телефоны', callback_data='phones'),
|
||||||
)
|
)
|
||||||
|
|
@ -18,15 +18,74 @@ def get_main_menu() -> InlineKeyboardMarkup:
|
||||||
)
|
)
|
||||||
builder.row(
|
builder.row(
|
||||||
InlineKeyboardButton(text='📅 График отключений', callback_data='schedule'),
|
InlineKeyboardButton(text='📅 График отключений', callback_data='schedule'),
|
||||||
InlineKeyboardButton(text='📢 Объявления', callback_data='announcements'),
|
)
|
||||||
|
builder.row(
|
||||||
|
InlineKeyboardButton(text='🏆 Достижения', callback_data='achievements'),
|
||||||
|
InlineKeyboardButton(text='📅 События', callback_data='events'),
|
||||||
|
)
|
||||||
|
builder.row(
|
||||||
|
InlineKeyboardButton(text='💰 Платежи', callback_data='payments'),
|
||||||
|
InlineKeyboardButton(text='📋 Опросы', callback_data='polls'),
|
||||||
)
|
)
|
||||||
builder.row(
|
builder.row(
|
||||||
InlineKeyboardButton(text='🔍 Моя квартира', callback_data='my_apartment'),
|
InlineKeyboardButton(text='🔍 Моя квартира', callback_data='my_apartment'),
|
||||||
InlineKeyboardButton(text='📣 Подать объявление', callback_data='submit_ad'),
|
|
||||||
)
|
)
|
||||||
builder.row(
|
builder.row(
|
||||||
|
InlineKeyboardButton(text='🙏 Поблагодарить', callback_data='thank_user'),
|
||||||
InlineKeyboardButton(text='❓ Помощь', callback_data='help'),
|
InlineKeyboardButton(text='❓ Помощь', callback_data='help'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return builder.as_markup()
|
||||||
|
|
||||||
|
|
||||||
|
def get_thank_user_keyboard(users: list, page: int = 0, page_size: int = 20) -> InlineKeyboardMarkup:
|
||||||
|
"""
|
||||||
|
Клавиатура для выбора пользователя чтобы поблагодарить
|
||||||
|
|
||||||
|
:param users: список пользователей (User модели)
|
||||||
|
:param page: текущая страница (0-based)
|
||||||
|
:param page_size: размер страницы
|
||||||
|
"""
|
||||||
|
builder = InlineKeyboardBuilder()
|
||||||
|
|
||||||
|
total_pages = (len(users) + page_size - 1) // page_size if users else 1
|
||||||
|
start_idx = page * page_size
|
||||||
|
end_idx = min(start_idx + page_size, len(users))
|
||||||
|
page_users = users[start_idx:end_idx]
|
||||||
|
|
||||||
|
for user in page_users:
|
||||||
|
name = user.full_name or f'user{user.user_id}'
|
||||||
|
apt = f' (кв. {user.apartment})' if user.apartment else ''
|
||||||
|
builder.button(
|
||||||
|
text=f'🙏 {name}{apt}',
|
||||||
|
callback_data=f'thank_select_{user.user_id}'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Кнопки навигации
|
||||||
|
nav_buttons = []
|
||||||
|
if page > 0:
|
||||||
|
nav_buttons.append(InlineKeyboardButton(text='⬅️', callback_data=f'thank_page_{page - 1}'))
|
||||||
|
if page < total_pages - 1:
|
||||||
|
nav_buttons.append(InlineKeyboardButton(text='➡️', callback_data=f'thank_page_{page + 1}'))
|
||||||
|
|
||||||
|
if nav_buttons:
|
||||||
|
builder.row(*nav_buttons)
|
||||||
|
|
||||||
|
builder.row(
|
||||||
|
InlineKeyboardButton(text='🔙 Назад', callback_data='main_menu'),
|
||||||
|
)
|
||||||
|
|
||||||
|
return builder.as_markup()
|
||||||
|
|
||||||
|
|
||||||
|
def get_thank_confirm_keyboard(user_id: int) -> InlineKeyboardMarkup:
|
||||||
|
"""Клавиатура подтверждения благодарности"""
|
||||||
|
builder = InlineKeyboardBuilder()
|
||||||
|
|
||||||
|
builder.row(
|
||||||
|
InlineKeyboardButton(text='✅ Поблагодарить', callback_data=f'thank_confirm_{user_id}'),
|
||||||
|
InlineKeyboardButton(text='❌ Отмена', callback_data='thank_user'),
|
||||||
|
)
|
||||||
|
|
||||||
return builder.as_markup()
|
return builder.as_markup()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue