MVP ready. Fully functional (registration? moderation, profiles vew)
Some checks reported errors
continuous-integration/drone/push Build encountered an error

This commit is contained in:
2025-08-12 21:55:56 +09:00
parent b282c44e7c
commit 9af84db429
17 changed files with 782 additions and 18 deletions

View 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)