⚡ АВТОВЕРИФИКАЦИЯ АКТИВНЫХ ПОЛЬЗОВАТЕЛЕЙ
✅ Реализовано: - Автоверификация по 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_'))
|
@router.callback_query(F.data.startswith('user_action_page_'))
|
||||||
@admin_only
|
@admin_only
|
||||||
async def cb_user_action_page(callback: CallbackQuery):
|
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_unverify'),
|
||||||
InlineKeyboardButton(text='📋 Список не вериф.', callback_data='admin_unverified_list'),
|
InlineKeyboardButton(text='📋 Список не вериф.', callback_data='admin_unverified_list'),
|
||||||
)
|
)
|
||||||
|
builder.row(
|
||||||
|
InlineKeyboardButton(text='⚡ Автоверификация', callback_data='admin_auto_verify'),
|
||||||
|
)
|
||||||
builder.row(
|
builder.row(
|
||||||
InlineKeyboardButton(text='❓ Помощь', callback_data='admin_help'),
|
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}%")))
|
(User.apartment.ilike(f"%{search}%")))
|
||||||
stmt = stmt.order_by(User.user_id.desc()).limit(100)
|
stmt = stmt.order_by(User.user_id.desc()).limit(100)
|
||||||
users = list((await session.execute(stmt)).scalars().all())
|
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",
|
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")
|
@app.post("/api/user/{user_id}/update")
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,64 @@
|
||||||
<div class="col-md-10 content">
|
<div class="col-md-10 content">
|
||||||
<h2 class="mb-4">Пользователи</h2>
|
<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 mb-4">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form method="GET" action="/users" class="row g-3">
|
<form method="GET" action="/users" class="row g-3">
|
||||||
|
|
@ -248,6 +305,31 @@
|
||||||
location.reload();
|
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>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue