MVP ready. Fully functional (registration? moderation, profiles vew)
Some checks reported errors
continuous-integration/drone/push Build encountered an error
Some checks reported errors
continuous-integration/drone/push Build encountered an error
This commit is contained in:
141
services/bot/app/handlers/admin.py
Normal file
141
services/bot/app/handlers/admin.py
Normal file
@@ -0,0 +1,141 @@
|
||||
from __future__ import annotations
|
||||
from telegram import Update
|
||||
from telegram.ext import ContextTypes
|
||||
from sqlalchemy import desc
|
||||
|
||||
from app.core.db import db_session
|
||||
from app.repositories.admin_repo import AdminRepository
|
||||
from app.repositories.candidate_repo import CandidateRepository
|
||||
from app.models.candidate import Candidate
|
||||
|
||||
from app.utils.common import csv_to_list
|
||||
|
||||
async def add_admin_handler(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
"""Команда /addadmin <telegram_id> — добавить нового администратора (только для админов)."""
|
||||
with db_session() as s:
|
||||
repo = AdminRepository(s)
|
||||
if not repo.is_admin(update.effective_user.id):
|
||||
await update.message.reply_text("Недостаточно прав.")
|
||||
return
|
||||
|
||||
args = context.args or []
|
||||
if not args:
|
||||
await update.message.reply_text("Формат: /addadmin 123456789")
|
||||
return
|
||||
|
||||
try:
|
||||
tid = int(args[0])
|
||||
except ValueError:
|
||||
await update.message.reply_text("Неверный telegram_id.")
|
||||
return
|
||||
|
||||
from app.models.admin import Admin
|
||||
if s.query(Admin).filter(Admin.telegram_id == tid).first():
|
||||
await update.message.reply_text("Этот администратор уже добавлен.")
|
||||
return
|
||||
|
||||
s.add(Admin(telegram_id=tid))
|
||||
s.commit()
|
||||
await update.message.reply_text(f"Администратор {tid} добавлен.")
|
||||
|
||||
|
||||
async def list_candidates_handler(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
"""Команда /candidates — показать последние анкеты (только для админов)."""
|
||||
with db_session() as s:
|
||||
admin_repo = AdminRepository(s)
|
||||
if not admin_repo.is_admin(update.effective_user.id):
|
||||
await update.message.reply_text("Недостаточно прав.")
|
||||
return
|
||||
|
||||
rows = s.query(Candidate).order_by(desc(Candidate.created_at)).limit(30).all()
|
||||
if not rows:
|
||||
await update.message.reply_text("Кандидатов пока нет.")
|
||||
return
|
||||
|
||||
out = []
|
||||
for c in rows:
|
||||
out.append(
|
||||
f"#{c.id} {c.full_name or '—'} | {c.city or '—'} | цель: {c.goal or '—'} | @{c.username or ''}"
|
||||
)
|
||||
await update.message.reply_text("Последние анкеты:\n" + "\n".join(out))
|
||||
|
||||
|
||||
async def verify_candidate_handler(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
"""Команда /verify <id> — пометить анкету как проверенную (только для админов)."""
|
||||
with db_session() as s:
|
||||
admin_repo = AdminRepository(s)
|
||||
if not admin_repo.is_admin(update.effective_user.id):
|
||||
await update.message.reply_text("Недостаточно прав.")
|
||||
return
|
||||
|
||||
args = context.args or []
|
||||
if not args:
|
||||
await update.message.reply_text("Формат: /verify 12")
|
||||
return
|
||||
|
||||
try:
|
||||
cid = int(args[0])
|
||||
except ValueError:
|
||||
await update.message.reply_text("Укажи числовой id анкеты.")
|
||||
return
|
||||
|
||||
c = s.get(Candidate, cid)
|
||||
if not c:
|
||||
await update.message.reply_text("Анкета не найдена.")
|
||||
return
|
||||
|
||||
c.is_verified = True
|
||||
s.commit()
|
||||
await update.message.reply_text(f"Анкета #{cid} помечена как проверенная.")
|
||||
|
||||
async def view_candidate_handler(update, context):
|
||||
"""Команда /view <id> — показать карточку анкеты с фото (для админов)."""
|
||||
with db_session() as s:
|
||||
admin_repo = AdminRepository(s)
|
||||
if not admin_repo.is_admin(update.effective_user.id):
|
||||
await update.message.reply_text("Недостаточно прав.")
|
||||
return
|
||||
|
||||
args = context.args or []
|
||||
if not args:
|
||||
await update.message.reply_text("Формат: /view 12")
|
||||
return
|
||||
|
||||
try:
|
||||
cid = int(args[0])
|
||||
except ValueError:
|
||||
await update.message.reply_text("Укажи числовой id анкеты.")
|
||||
return
|
||||
|
||||
c = s.get(Candidate, cid)
|
||||
if not c:
|
||||
await update.message.reply_text("Анкета не найдена.")
|
||||
return
|
||||
|
||||
# Текстовая карточка
|
||||
caption = (
|
||||
f"#{c.id} {c.full_name or '—'} @{c.username or ''}\n"
|
||||
f"Город: {c.city or '—'} | Цель: {c.goal or '—'}\n"
|
||||
f"Статус: {'✅ проверена' if c.is_verified else '⏳ на проверке'} | Активна: {'да' if c.is_active else 'нет'}"
|
||||
)
|
||||
|
||||
chat_id = update.effective_chat.id
|
||||
|
||||
# Аватар (если есть)
|
||||
if c.avatar_file_id:
|
||||
await context.bot.send_photo(chat_id=chat_id, photo=c.avatar_file_id, caption=caption)
|
||||
else:
|
||||
await context.bot.send_message(chat_id=chat_id, text=caption + "\n(аватар отсутствует)")
|
||||
|
||||
# Галерея (если есть)
|
||||
gallery_ids = csv_to_list(c.gallery_file_ids)
|
||||
if gallery_ids:
|
||||
# Telegram принимает до 10 media за раз
|
||||
chunk = []
|
||||
for fid in gallery_ids:
|
||||
chunk.append(InputMediaPhoto(media=fid))
|
||||
if len(chunk) == 10:
|
||||
await context.bot.send_media_group(chat_id=chat_id, media=chunk)
|
||||
chunk = []
|
||||
if chunk:
|
||||
await context.bot.send_media_group(chat_id=chat_id, media=chunk)
|
||||
Reference in New Issue
Block a user