"""Sending things to a student from anywhere (button, deep link, after onboarding).""" import logging from aiogram import Bot from aiogram.exceptions import TelegramBadRequest from sqlalchemy.ext.asyncio import AsyncSession from app.bot.keyboards import library as lib_kb from app.bot.keyboards.common import share_kb from app.core import season from app.core.i18n import t from app.db.models import ActivityEvent, Material, User from app.db.repos import library as repo from app.services import growth from app.services import library as lib from app.services import xp as xp_service log = logging.getLogger(__name__) async def _maybe_ask_to_share(bot: Bot, user: User, chat_id: int) -> None: """At the moment a student just got something useful, suggest sharing: at most once a day, never on their first day.""" today = season.day_key() if (user.active_days or 0) < 2 or user.share_prompt_day == today: return user.share_prompt_day = today me = await bot.me() link = growth.deep_link(me.username, str(user.id)) # their personal referral link url = growth.share_url(link, t(user.lang, "share_text")) try: await bot.send_message(chat_id, t(user.lang, "share_prompt"), reply_markup=share_kb(user.lang, url)) except Exception as exc: log.info("Could not send the share nudge: %r", exc) async def deliver_material(bot: Bot, session: AsyncSession, user: User, material: Material, chat_id: int) -> bool: course = await repo.get_course(session, material.course_id) if material.course_id else None try: await lib.send_material(bot, chat_id, material, course) except TelegramBadRequest: return False material.downloads = (material.downloads or 0) + 1 session.add(ActivityEvent(user_id=user.id, kind="download", ref=str(material.id), n=1)) res = await xp_service.award(session, user, "download", 1, ref=f"{material.id}:{season.day_key()}") if res.leveled_up: await xp_service.announce_level(bot, user, res.new_level) await _maybe_ask_to_share(bot, user, chat_id) return True async def deliver_link(bot: Bot, session: AsyncSession, user: User, chat_id: int, kind: str, value: str) -> bool: """Open what a shared link points at (a course or one file). False = nothing to show.""" if kind == "course": course, counts = await lib.kinds_of(session, int(value)) if course and any(counts.values()): await bot.send_message( chat_id, t(user.lang, "lib_kinds", course=course.name), reply_markup=lib_kb.kinds(user.lang, course.id, counts) ) return True elif kind == "file": material = await repo.get_material(session, int(value)) if material and material.file_id and material.status == "ok": return await deliver_material(bot, session, user, material, chat_id) return False async def deliver_pending(bot: Bot, session: AsyncSession, user: User, chat_id: int) -> None: """The link someone opened before they had joined / finished onboarding.""" if not user.pending_link: return kind, value = growth.parse_start_payload(user.pending_link) user.pending_link = None await deliver_link(bot, session, user, chat_id, kind, value)