⚡ АВТОВЕРИФИКАЦИЯ АКТИВНЫХ ПОЛЬЗОВАТЕЛЕЙ
✅ Реализовано: - Автоверификация по 50+ сообщениям - Кнопка админу '⚡ Автоверификация' - Веб-админка → Пользователи → Статистика + Кнопка - Плавный ввод верификации 🎯 Как работает: 1. Бот считает сообщения пользователей 2. Кто набрал 50+ сообщений — считается 'своим' 3. Админ жмёт кнопку → все активные верифицируются 4. Веб-админка показывает статистику: - Всего пользователей - Верифицировано - Не верифицировано - Активных для верификации 📝 Преимущества: - Не нужно проверять 200 человек вручную - Активные жильцы сразу получают доступ - Админы проверяют только подозрительных - Мотивация: телефоны только после верификации 🧪 Тест: /admin → ⚡ Автоверификация или Веб-админка → Пользователи → Верифицировать всех 💡 Плавный ввод: - Бот предлагает верификацию, но не требует - Телефоны и функции только после верификации - Жильцы сами мотивированы пройти верификацию Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
parent
5adb3d7818
commit
d81ee6d238
4 changed files with 158 additions and 4 deletions
|
|
@ -1161,6 +1161,44 @@ async def cb_admin_user_actions(callback: CallbackQuery):
|
|||
)
|
||||
|
||||
|
||||
@router.callback_query(F.data == 'admin_auto_verify')
|
||||
@admin_only
|
||||
async def cb_admin_auto_verify(callback: CallbackQuery):
|
||||
"""Автоверификация активных пользователей"""
|
||||
await callback.answer()
|
||||
|
||||
async with AsyncSessionLocal() as session:
|
||||
# Находим активных пользователей (50+ сообщений) которые не верифицированы
|
||||
stmt = select(User).where(
|
||||
User.verified == False,
|
||||
User.message_count >= 50
|
||||
).order_by(desc(User.message_count))
|
||||
result = await session.execute(stmt)
|
||||
active_users = list(result.scalars().all())
|
||||
|
||||
if not active_users:
|
||||
await callback.message.answer('✅ Нет активных пользователей для верификации')
|
||||
return
|
||||
|
||||
# Верифицируем всех
|
||||
verified_count = 0
|
||||
for user in active_users:
|
||||
user.verified = True
|
||||
if not user.verification_date:
|
||||
user.verification_date = datetime.utcnow()
|
||||
verified_count += 1
|
||||
|
||||
await session.commit()
|
||||
|
||||
await callback.message.answer(
|
||||
f'✅ <b>Автоверификация завершена!</b>\n\n'
|
||||
f'Верифицировано: {verified_count} пользователей\n'
|
||||
f'Критерий: 50+ сообщений\n\n'
|
||||
f'Теперь они имеют полный доступ ко всем функциям бота.',
|
||||
parse_mode='HTML'
|
||||
)
|
||||
|
||||
|
||||
@router.callback_query(F.data.startswith('user_action_page_'))
|
||||
@admin_only
|
||||
async def cb_user_action_page(callback: CallbackQuery):
|
||||
|
|
|
|||
|
|
@ -43,6 +43,9 @@ def get_admin_main_menu() -> InlineKeyboardMarkup:
|
|||
InlineKeyboardButton(text='🚫 Отозвать вериф.', callback_data='admin_unverify'),
|
||||
InlineKeyboardButton(text='📋 Список не вериф.', callback_data='admin_unverified_list'),
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(text='⚡ Автоверификация', callback_data='admin_auto_verify'),
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(text='❓ Помощь', callback_data='admin_help'),
|
||||
)
|
||||
|
|
|
|||
33
web/app.py
33
web/app.py
|
|
@ -84,8 +84,39 @@ async def users_page(request: Request, search: str = "", username: str = Depends
|
|||
(User.apartment.ilike(f"%{search}%")))
|
||||
stmt = stmt.order_by(User.user_id.desc()).limit(100)
|
||||
users = list((await session.execute(stmt)).scalars().all())
|
||||
|
||||
# Статистика
|
||||
total_count = (await session.execute(select(func.count(User.user_id)))).scalar()
|
||||
verified_count = (await session.execute(select(func.count(User.user_id)).where(User.verified == True))).scalar()
|
||||
active_unverified = (await session.execute(
|
||||
select(func.count(User.user_id)).where(User.verified == False, User.message_count >= 50)
|
||||
)).scalar()
|
||||
|
||||
return templates.TemplateResponse("users.html",
|
||||
{"request": request, "username": username, "users": users, "search": search})
|
||||
{"request": request, "username": username, "users": users, "search": search,
|
||||
"stats": {"total": total_count, "verified": verified_count, "unverified": total_count - verified_count, "active_unverified": active_unverified}})
|
||||
|
||||
|
||||
@app.post("/api/users/auto_verify")
|
||||
async def api_auto_verify_users(username: str = Depends(get_current_admin)):
|
||||
"""Автоверификация активных пользователей"""
|
||||
async with AsyncSessionLocal() as session:
|
||||
# Находим активных (50+ сообщений) которые не верифицированы
|
||||
stmt = select(User).where(User.verified == False, User.message_count >= 50)
|
||||
result = await session.execute(stmt)
|
||||
active_users = list(result.scalars().all())
|
||||
|
||||
# Верифицируем всех
|
||||
verified_count = 0
|
||||
for user in active_users:
|
||||
user.verified = True
|
||||
if not user.verification_date:
|
||||
user.verification_date = datetime.utcnow()
|
||||
verified_count += 1
|
||||
|
||||
await session.commit()
|
||||
|
||||
return JSONResponse({"success": True, "message": f"Верифицировано {verified_count} активных пользователей"})
|
||||
|
||||
|
||||
@app.post("/api/user/{user_id}/update")
|
||||
|
|
|
|||
|
|
@ -36,7 +36,64 @@
|
|||
<div class="col-md-10 content">
|
||||
<h2 class="mb-4">Пользователи</h2>
|
||||
|
||||
<!-- Search -->
|
||||
<!-- Stats -->
|
||||
{% if stats %}
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-3">
|
||||
<div class="card bg-primary text-white">
|
||||
<div class="card-body text-center">
|
||||
<h3>{{ stats.total }}</h3>
|
||||
<small>Всего пользователей</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card bg-success text-white">
|
||||
<div class="card-body text-center">
|
||||
<h3>{{ stats.verified }}</h3>
|
||||
<small>Верифицировано</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card bg-warning text-dark">
|
||||
<div class="card-body text-center">
|
||||
<h3>{{ stats.unverified }}</h3>
|
||||
<small>Не верифицировано</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card bg-info text-white">
|
||||
<div class="card-body text-center">
|
||||
<h3>{{ stats.active_unverified }}</h3>
|
||||
<small>Активных (50+ сообщений)</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Auto Verify Button -->
|
||||
{% if stats and stats.active_unverified > 0 %}
|
||||
<div class="card mb-4 bg-light">
|
||||
<div class="card-body">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-8">
|
||||
<h5 class="mb-0">⚡ Автоверификация активных</h5>
|
||||
<small class="text-muted">
|
||||
Верифицировать {{ stats.active_unverified }} пользователей с 50+ сообщениями
|
||||
</small>
|
||||
</div>
|
||||
<div class="col-md-4 text-end">
|
||||
<button class="btn btn-success" onclick="autoVerify()">
|
||||
<i class="bi bi-lightning-charge"></i> Верифицировать всех
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
<form method="GET" action="/users" class="row g-3">
|
||||
|
|
@ -248,6 +305,31 @@
|
|||
location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
async function autoVerify() {
|
||||
if (!confirm('Верифицировать всех активных пользователей (50+ сообщений)?')) return;
|
||||
|
||||
const resultDiv = document.getElementById('autoVerifyResult');
|
||||
if (resultDiv) {
|
||||
resultDiv.innerHTML = '<div class="alert alert-info">Автоверификация...</div>';
|
||||
} else {
|
||||
alert('Автоверификация...');
|
||||
}
|
||||
|
||||
try {
|
||||
const r = await fetch('/api/users/auto_verify', { method: 'POST' });
|
||||
const result = await r.json();
|
||||
|
||||
if (result.success) {
|
||||
alert('✅ ' + result.message);
|
||||
location.reload();
|
||||
} else {
|
||||
alert('❌ Ошибка: ' + (result.error || 'Неизвестная'));
|
||||
}
|
||||
} catch (err) {
|
||||
alert('❌ Ошибка: ' + err.message);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Reference in a new issue