v1.22 Безопасность - белый список
🔒 БЕЗОПАСНОСТЬ: • Бот работает ТОЛЬКО с пользователями из базы • /start - игнор если нет в базе • /help - игнор если нет в базе • Верифицированные - полный доступ • Неверифицированные - стимул пройти верификацию 📁 ФАЙЛЫ: • handlers/users.py: проверка базы • collect_all_members.py: скрипт сбора 🎯 СТИМУЛ: • Хочешь бота? → Пройди верификацию! • Админы, ИГ, верифицированные - работают • Остальные - игнор Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
parent
049a152dc8
commit
9cee4e8d1f
4 changed files with 132 additions and 64 deletions
117
collect_all_members.py
Normal file
117
collect_all_members.py
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Сбор всех участников группы в базу данных
|
||||
Использование: python collect_all_members.py
|
||||
"""
|
||||
import asyncio
|
||||
import logging
|
||||
import aiohttp
|
||||
from aiohttp_socks import ProxyConnector
|
||||
from sqlalchemy import select, func
|
||||
from database.db import AsyncSessionLocal, init_db
|
||||
from database.models import User
|
||||
from config import BOT_TOKEN, ADMIN_CHAT_ID, get_proxy_url
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def collect_all_members():
|
||||
"""Собрать всех участников группы"""
|
||||
|
||||
await init_db()
|
||||
|
||||
proxy_url = get_proxy_url()
|
||||
connector = ProxyConnector.from_url(proxy_url)
|
||||
|
||||
async with aiohttp.ClientSession(connector=connector) as session:
|
||||
# Получаем всех участников через getChatMembers
|
||||
# Telegram API позволяет получать по 100 за раз
|
||||
all_members = []
|
||||
offset = 0
|
||||
limit = 100
|
||||
|
||||
logger.info(f'🔄 Начинаем сбор участников...')
|
||||
|
||||
while True:
|
||||
try:
|
||||
url = f"https://api.telegram.org/bot{BOT_TOKEN}/getChatMembers"
|
||||
params = {
|
||||
"chat_id": ADMIN_CHAT_ID,
|
||||
"offset": offset,
|
||||
"limit": limit
|
||||
}
|
||||
|
||||
async with session.get(url, params=params) as resp:
|
||||
result = await resp.json()
|
||||
|
||||
if not result.get('ok'):
|
||||
logger.error(f'Ошибка API: {result}')
|
||||
break
|
||||
|
||||
members = result.get('result', [])
|
||||
if not members:
|
||||
break
|
||||
|
||||
all_members.extend(members)
|
||||
offset += limit
|
||||
|
||||
logger.info(f'Загружено {len(all_members)} участников...')
|
||||
|
||||
# Если загрузили меньше чем лимит — это конец
|
||||
if len(members) < limit:
|
||||
break
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f'Ошибка при загрузке: {e}')
|
||||
break
|
||||
|
||||
logger.info(f'✅ Всего загружено: {len(all_members)}')
|
||||
|
||||
# Добавляем в базу
|
||||
async with AsyncSessionLocal() as db_session:
|
||||
added = 0
|
||||
updated = 0
|
||||
|
||||
for member in all_members:
|
||||
user_data = member.get('user', {})
|
||||
user_id = user_data.get('id')
|
||||
|
||||
if not user_id:
|
||||
continue
|
||||
|
||||
user = await db_session.get(User, user_id)
|
||||
|
||||
if not user:
|
||||
# Новый пользователь
|
||||
user = User(
|
||||
user_id=user_id,
|
||||
username=user_data.get('username'),
|
||||
first_name=user_data.get('first_name'),
|
||||
last_name=user_data.get('last_name'),
|
||||
verified=False, # Пока не верифицирован
|
||||
)
|
||||
db_session.add(user)
|
||||
added += 1
|
||||
else:
|
||||
# Обновляем данные
|
||||
if user_data.get('username') and user.username != user_data.get('username'):
|
||||
user.username = user_data.get('username')
|
||||
updated += 1
|
||||
if user_data.get('first_name') and user.first_name != user_data.get('first_name'):
|
||||
user.first_name = user_data.get('first_name')
|
||||
updated += 1
|
||||
|
||||
await db_session.commit()
|
||||
|
||||
# Статистика
|
||||
total = (await db_session.execute(select(func.count(User.user_id)))).scalar()
|
||||
|
||||
logger.info(f'✅ Сбор завершён!')
|
||||
logger.info(f' Добавлено: {added}')
|
||||
logger.info(f' Обновлено: {updated}')
|
||||
logger.info(f' Всего в базе: {total}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(collect_all_members())
|
||||
BIN
data/service_images/police_2a8a48fc857d4cf78a10583d6820f1be.png
Normal file
BIN
data/service_images/police_2a8a48fc857d4cf78a10583d6820f1be.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 MiB |
BIN
data/service_images/utility_508733a4ff614d18be9424936c45b57f.png
Normal file
BIN
data/service_images/utility_508733a4ff614d18be9424936c45b57f.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 MiB |
|
|
@ -24,15 +24,9 @@ router = Router()
|
|||
async def cmd_start(message: Message):
|
||||
"""Команда /start (ТОЛЬКО В ЛИЧКЕ!)"""
|
||||
from utils.ui_styles import get_level_badge
|
||||
from aiogram.types import ReplyKeyboardMarkup, KeyboardButton
|
||||
|
||||
# Проверяем что это личка
|
||||
if message.chat.type != 'private':
|
||||
await message.answer(
|
||||
'💡 <b>Бот работает только в личных сообщениях!</b>\n\n'
|
||||
'Нажмите на имя бота сверху и напишите /start в личке',
|
||||
parse_mode='HTML'
|
||||
)
|
||||
return
|
||||
|
||||
user_id = message.from_user.id
|
||||
|
|
@ -44,62 +38,14 @@ async def cmd_start(message: Message):
|
|||
user = result.scalar_one_or_none()
|
||||
|
||||
if not user:
|
||||
# Создаём нового пользователя
|
||||
user = User(
|
||||
user_id=user_id,
|
||||
username=message.from_user.username,
|
||||
first_name=message.from_user.first_name,
|
||||
last_name=message.from_user.last_name,
|
||||
)
|
||||
session.add(user)
|
||||
await session.commit()
|
||||
# Пользователя нет в базе - ИГНОР!
|
||||
logger.info(f'Пользователь {user_id} не в базе - игнорируем /start')
|
||||
return
|
||||
|
||||
# 🔴 НОВОЕ СООБЩЕНИЕ С КНОПКАМИ (БЕЗ <font>!)
|
||||
welcome_text = f'''🔴 <b>ДОБРО ПОЖАЛОВАТЬ</b>
|
||||
|
||||
👋 <b>Привет, {message.from_user.first_name}!</b>
|
||||
|
||||
Я — домовой бот нашего дома.
|
||||
|
||||
🏠 <b>Что я умею:</b>
|
||||
• 📞 Телефоны экстренных служб
|
||||
• 👷 Проверенные мастера
|
||||
• 📅 График отключений
|
||||
• 💰 Напоминания о платежах
|
||||
• 🏆 Рейтинг и достижения
|
||||
• 📋 Опросы и голосования
|
||||
|
||||
⚠️ <b>ВАЖНО:</b>
|
||||
Для полного доступа пройдите верификацию!
|
||||
|
||||
📝 <b>Как пройти верификацию:</b>
|
||||
|
||||
<b>Шаг 1:</b> Отправьте номер квартиры
|
||||
Примеры: <code>45</code>, <code>12А</code>, <code>100-1</code>
|
||||
|
||||
<b>Шаг 2:</b> (опционально) Отправьте телефон
|
||||
Пример: <code>+79991234567</code>
|
||||
|
||||
<b>Шаг 3:</b> Дождитесь подтверждения админа
|
||||
|
||||
После верификации откроются все функции! 🎉'''
|
||||
|
||||
# Кнопки для быстрого старта
|
||||
kb = ReplyKeyboardMarkup(
|
||||
keyboard=[
|
||||
[KeyboardButton(text='📞 Экстренные телефоны')],
|
||||
[KeyboardButton(text='📜 Правила чата'), KeyboardButton(text='❓ Помощь')],
|
||||
],
|
||||
resize_keyboard=True
|
||||
)
|
||||
|
||||
await message.answer(welcome_text, parse_mode='HTML', reply_markup=kb)
|
||||
|
||||
else:
|
||||
# Обновляем имя
|
||||
user.username = message.from_user.username
|
||||
user.first_name = message.from_user.first_name
|
||||
user.last_name = message.from_user.last_name
|
||||
# Пользователь в базе - обновляем имя
|
||||
user.username = message.from_user.username
|
||||
user.first_name = message.from_user.first_name
|
||||
user.last_name = message.from_user.last_name
|
||||
|
||||
# Проверяем статус
|
||||
is_admin = user.is_admin or (user_id == config.ADMIN_USER_ID)
|
||||
|
|
@ -182,15 +128,20 @@ async def cmd_start(message: Message):
|
|||
@router.message(Command('help'))
|
||||
async def cmd_help(message: Message):
|
||||
"""Справка по командам (для жильцов)"""
|
||||
from utils.ui_styles import get_colored_header, get_level_badge, get_user_level_with_ig
|
||||
from utils.ui_styles import get_colored_header, get_level_badge
|
||||
from database.models import InitiativeGroup
|
||||
|
||||
user_id = message.from_user.id
|
||||
async with AsyncSessionLocal() as session:
|
||||
user = await session.get(User, user_id)
|
||||
|
||||
# Проверяем есть ли в базе
|
||||
if not user:
|
||||
logger.info(f'Пользователь {user_id} не в базе - игнорируем /help')
|
||||
return
|
||||
|
||||
# Определяем уровень
|
||||
if user and user.verified:
|
||||
if user.verified:
|
||||
ig_stmt = select(InitiativeGroup).where(
|
||||
InitiativeGroup.user_id == user_id,
|
||||
InitiativeGroup.is_active == True
|
||||
|
|
|
|||
Loading…
Reference in a new issue