update sign up notifications
Build and Push Docker Images / Build and Push Docker Images (push) Failing after 9s

This commit is contained in:
2026-08-02 14:54:00 +05:00
parent 1f5e60dbb4
commit 1ea2f9f4c2
3 changed files with 88 additions and 16 deletions
+20
View File
@@ -70,6 +70,26 @@ async def send_telegram_alert(markdown_message: str):
)
async def send_telegram_photo(photo, markdown_caption: str):
"""Send a photo. `photo` may be raw bytes / a file-like object (uploaded
directly to Telegram) or a public URL string."""
logger.info("[TELEGRAM] Preparing to send photo...")
kwargs = {}
if TOPIC_ID:
kwargs["message_thread_id"] = int(TOPIC_ID)
await bot.send_photo(
chat_id=str(CHAT_ID),
photo=photo,
caption=markdown_caption,
parse_mode=ParseMode.MARKDOWN_V2,
# Uploading media is slower than a text send; the defaults (5s) time out.
connect_timeout=15,
read_timeout=30,
write_timeout=60,
**kwargs,
)
def escape_markdown_v2(text: str) -> str:
escape_chars = r"_~`>#+-=|{}.!\\"
return re.sub(f"([{re.escape(escape_chars)}])", r"\\\1", text)
+3 -1
View File
@@ -8,7 +8,9 @@ class Command(BaseCommand):
help = "Seeds baseline reference data (atolls and islands) required for sign up."
def handle(self, *args, **options):
atoll, atoll_created = Atoll.objects.get_or_create(name="Faafu")
# Atoll name must match the person-verify API's `atoll_en` (the atoll
# code letter, e.g. "F" for Faafu) or user verification fails.
atoll, atoll_created = Atoll.objects.get_or_create(name="F")
self.stdout.write(
self.style.SUCCESS(f"Created atoll: {atoll.name}")
if atoll_created
+65 -15
View File
@@ -9,13 +9,20 @@ from django.utils import timezone
# from api.notifications import send_clean_telegram_markdown
from api.omada import Omada
from api.bot import send_telegram_alert, telegram_loop, escape_markdown_v2
from api.bot import (
send_telegram_alert,
send_telegram_photo,
telegram_loop,
escape_markdown_v2,
)
import asyncio
from apibase.env import env, BASE_DIR
from procrastinate.contrib.django import app
from procrastinate import builtin_tasks
import time
import io
import requests
from PIL import Image
logger = logging.getLogger(__name__)
@@ -163,10 +170,58 @@ def verify_user_with_person_api_task(user_id: int):
response = requests.get(f"{PERSON_VERIFY_BASE_URL}/api/person/{t_user.t_id_card}")
verification_failed_message = f"""*The following user verification failed*:\n\n*ID Card:* {t_user.t_id_card}\n*Name:* {t_user.t_first_name} {t_user.t_last_name}\n*House Name:* {t_user.t_address}\n*Date of Birth:* {t_user.t_dob}\n*Island:* {(t_user.t_atoll.name if t_user.t_atoll else "N/A")} {(t_user.t_island.name if t_user.t_island else "N/A")}\n*Mobile:* {t_user.t_mobile}\nVisit [SAR Link Portal](https://portal.sarlink.net/users/{user_id}/details) to manually verify this user.
"""
user_details = (
f"*NID:* {t_user.t_id_card}\n"
f"*Name:* {t_user.t_first_name} {t_user.t_last_name}\n"
f"*Phone:* {t_user.t_mobile}\n"
f"*Date of Birth:* {t_user.t_dob}\n"
f"*Address:* {t_user.t_address}\n"
f"*Island:* {(t_user.t_atoll.name if t_user.t_atoll else 'N/A')}. "
f"{(t_user.t_island.name if t_user.t_island else 'N/A')}"
)
logger.info(verification_failed_message)
verification_failed_message = (
f"⚠️ *User verification failed*\n\n{user_details}\n"
f"Visit [SAR Link Portal](https://portal.sarlink.net/users/{user_id}/details) "
f"to manually verify this user."
)
verification_success_message = (
f"✅ *New user registered and verified*\n\n{user_details}\n"
f"View on [SAR Link Portal](https://portal.sarlink.net/users/{user_id}/details)."
)
def _run(coro) -> None:
asyncio.run_coroutine_threadsafe(coro, telegram_loop).result()
def _fetch_photo(url: str) -> io.BytesIO:
"""Download the ID photo and convert it to a Telegram-safe JPEG.
The image lives on a private-network URL that Telegram's servers can't
reach, so we fetch the bytes here and upload them directly. Conversion
to JPEG avoids Telegram rejecting formats like webp.
"""
resp = requests.get(url, timeout=10)
resp.raise_for_status()
buf = io.BytesIO()
Image.open(io.BytesIO(resp.content)).convert("RGB").save(buf, format="JPEG")
buf.seek(0)
buf.name = "photo.jpg"
return buf
def send_telegram(message: str, photo_url: str | None = None) -> None:
caption = escape_markdown_v2(message)
if photo_url:
try:
_run(send_telegram_photo(_fetch_photo(photo_url), caption))
return
except Exception as e:
logger.warning(f"[Registration] TELEGRAM PHOTO ERROR: {e}")
# Fall through to a plain text alert below.
try:
_run(send_telegram_alert(markdown_message=caption))
except Exception as e:
logger.warning(f"[Registration] TELEGRAM ALERT ERROR: {e}")
if response.status_code == 200:
@@ -177,6 +232,7 @@ def verify_user_with_person_api_task(user_id: int):
api_dob = data.get("dob")
api_atoll = data.get("atoll_en")
api_island_name = data.get("island_name_en")
api_image_url = data.get("image_url")
if not t_user.t_mobile or t_user.t_dob is None:
logger.error("User mobile or date of birth is not set.")
@@ -230,22 +286,16 @@ def verify_user_with_person_api_task(user_id: int):
):
t_user.t_verified = True
t_user.save()
logger.info(verification_success_message)
send_telegram(verification_success_message, photo_url=api_image_url)
return True
else:
t_user.t_verified = False
t_user.save()
# send_clean_telegram_markdown(message=verification_failed_message)
try:
asyncio.run_coroutine_threadsafe(
send_telegram_alert(
markdown_message=escape_markdown_v2(verification_failed_message)
),
telegram_loop,
).result()
except Exception as e:
logger.warning("[Registration] TELEGRAM ALERT ERROR", e)
logger.info(verification_failed_message)
send_telegram(verification_failed_message, photo_url=api_image_url)
return False
else:
# Handle the error case