Spaces:
Sleeping
Sleeping
| import os | |
| import secrets | |
| import smtplib | |
| import logging | |
| from pathlib import Path | |
| from random import SystemRandom | |
| from email.mime.multipart import MIMEMultipart | |
| from email.mime.text import MIMEText | |
| from flask import Flask, request, jsonify, render_template | |
| logging.basicConfig(level=logging.INFO, format="%(levelname)s %(message)s") | |
| logger = logging.getLogger(__name__) | |
| app = Flask(__name__) | |
| app.secret_key = secrets.token_hex(32) | |
| WORDS_DIR = Path(__file__).parent / "words" | |
| def _load(path: Path) -> list[str]: | |
| words = [w.strip() for w in path.read_text().splitlines() if w.strip()] | |
| if not words: | |
| raise RuntimeError(f"Word list is empty: {path}") | |
| return words | |
| ADJECTIVES = _load(WORDS_DIR / "Positive-Adjective-List.txt") | |
| ANIMALS = _load(WORDS_DIR / "animals.txt") | |
| _rng = SystemRandom() | |
| def generate_pseudonym() -> str: | |
| return f"{_rng.choice(ADJECTIVES)} {_rng.choice(ANIMALS)}" | |
| def send_pseudonym_emails( | |
| *, | |
| sender_email: str, | |
| app_password: str, | |
| teacher_email: str, | |
| student_email: str, | |
| student_name: str, | |
| pseudonym: str, | |
| ) -> None: | |
| """Send notification emails via SMTP. Credentials live only in this | |
| function's local scope and are never written to disk or logged.""" | |
| subject = "Pseudonym Generation / Генерация псевдонима" | |
| teacher_body = ( | |
| f"Здравствуйте!\n\n" | |
| f"Студенту {student_name} был назначен псевдоним: {pseudonym}.\n\n" | |
| f"Это автоматическое уведомление. Хорошего дня!\n\n" | |
| f"---\n" | |
| f"Hello!\n\n" | |
| f"Student {student_name} has been assigned the pseudonym: {pseudonym}.\n\n" | |
| f"This is an automated notification. Have a great day!" | |
| ) | |
| student_body = ( | |
| f"Здравствуйте, {student_name}!\n\n" | |
| f"Вам был присвоен псевдоним: {pseudonym}.\n" | |
| f"Можете использовать его при получении своей оценки! \n\n" | |
| f"---\n" | |
| f"Hello, {student_name}!\n\n" | |
| f"You have been assigned the pseudonym: {pseudonym}.\n" | |
| f"Please use it when submitting your work.\n\n" | |
| f"This is an automated notification." | |
| ) | |
| def _build(to_addr: str, body: str) -> MIMEMultipart: | |
| msg = MIMEMultipart() | |
| msg["Subject"] = subject | |
| msg["From"] = sender_email | |
| msg["To"] = to_addr | |
| msg.attach(MIMEText(body, "plain", "utf-8")) | |
| return msg | |
| with smtplib.SMTP("smtp.gmail.com", 587) as smtp: | |
| smtp.ehlo() | |
| smtp.starttls() | |
| smtp.login(sender_email, app_password) | |
| smtp.sendmail(sender_email, teacher_email, _build(teacher_email, teacher_body).as_string()) | |
| smtp.sendmail(sender_email, student_email, _build(student_email, student_body).as_string()) | |
| def index(): | |
| return render_template("index.html") | |
| def send(): | |
| data = request.get_json(silent=True) or {} | |
| sender_email = (data.get("sender_email") or "").strip() | |
| app_password = (data.get("app_password") or "").strip() | |
| teacher_email = (data.get("teacher_email") or "").strip() | |
| student_email = (data.get("student_email") or "").strip() | |
| student_name = (data.get("student_name") or "").strip() | |
| errors = [] | |
| for field, value in [ | |
| ("sender_email", sender_email), | |
| ("app_password", app_password), | |
| ("teacher_email", teacher_email), | |
| ("student_email", student_email), | |
| ("student_name", student_name), | |
| ]: | |
| if not value: | |
| errors.append(f"{field} is required") | |
| if errors: | |
| return jsonify({"ok": False, "error": "; ".join(errors)}), 400 | |
| pseudonym = generate_pseudonym() | |
| try: | |
| send_pseudonym_emails( | |
| sender_email = sender_email, | |
| app_password = app_password, | |
| teacher_email = teacher_email, | |
| student_email = student_email, | |
| student_name = student_name, | |
| pseudonym = pseudonym, | |
| ) | |
| except smtplib.SMTPAuthenticationError: | |
| logger.warning("SMTP auth failed for sender (address redacted)") | |
| return jsonify({"ok": False, "error": "Authentication failed. Check your sender email and app password."}), 401 | |
| except smtplib.SMTPRecipientsRefused as exc: | |
| logger.warning("Recipient refused: %s", list(exc.recipients)) | |
| return jsonify({"ok": False, "error": "One or more recipient addresses were refused."}), 400 | |
| except Exception as exc: # noqa: BLE001 | |
| logger.exception("Unexpected SMTP error") | |
| return jsonify({"ok": False, "error": f"Could not send email: {exc}"}), 500 | |
| finally: | |
| app_password = None # noqa: F841 | |
| sender_email = None # noqa: F841 | |
| logger.info("Pseudonym '%s' dispatched!", pseudonym) | |
| return jsonify({"ok": True, "pseudonym": pseudonym}) | |
| if __name__ == "__main__": | |
| port = int(os.environ.get("PORT", 7860)) | |
| app.run(host="0.0.0.0", port=port, debug=False) |