import logging
from pathlib import Path
from typing import Literal, assert_never, cast
import telebot
from django.conf import settings
from cyber_valley.common.telegram import require_telegram_bot_token
from cyber_valley.notifications.helpers import send_notification
from cyber_valley.shaman_verification.models import VerificationRequest
from cyber_valley.users.models import UserSocials
log = logging.getLogger(__name__)
def create_verification_caption(
metadata_cid: str,
verification_type: str,
status: Literal["pending", "approved", "declined"] = "pending",
requester_chat_id: int | None = None,
requester_username: str | None = None,
) -> str:
ipfs_url = f"{settings.IPFS_PUBLIC_HOST}/ipfs/{metadata_cid}"
header: str
match status:
case "pending":
header = "๐ New Shaman Verification Request"
case "approved":
header = "โ
Verification request has been approved"
case "declined":
header = "โ Verification request has been declined"
case _ as unreachable:
assert_never(unreachable)
ipfs_link = f'<a href="{ipfs_url}">View on IPFS</a>'
caption_parts = [header, f"\nType: {verification_type}"]
if requester_chat_id:
requester_display = requester_username or "User"
requester_link = (
f'<a href="tg://user?id={requester_chat_id}">@{requester_display}</a>'
)
caption_parts.append(f"Requester: {requester_link}")
caption_parts.append(f"IPFS Metadata: {ipfs_link}")
return "\n".join(caption_parts)
def send_verification_request_to_provider(
chat_id: int, verification_request_id: int, username: str | None = None
) -> None:
token = require_telegram_bot_token()
bot = telebot.TeleBot(token)
try:
verification_request = VerificationRequest.objects.get(
id=verification_request_id
)
except VerificationRequest.DoesNotExist:
log.exception(
"Verification request %s not found, cannot send to provider",
verification_request_id,
)
return
assert verification_request.requester_id is not None
telegram_social = verification_request.requester.socials.filter(
network=UserSocials.Network.TELEGRAM
).first()
assert telegram_social is not None
requester_chat_id = int(telegram_social.value)
requester_username = (
telegram_social.metadata.get("username") if telegram_social.metadata else None
)
caption = create_verification_caption(
metadata_cid=verification_request.metadata_cid,
verification_type=verification_request.verification_type,
status="pending",
requester_chat_id=requester_chat_id,
requester_username=requester_username,
)
markup = telebot.types.InlineKeyboardMarkup()
markup.add(
telebot.types.InlineKeyboardButton(
"โ
Approve", callback_data=f"approve:{verification_request.id}"
),
telebot.types.InlineKeyboardButton(
"โ Decline", callback_data=f"decline:{verification_request.id}"
),
)
verification_path = (
settings.IPFS_DATA_PATH / "verifications" / str(verification_request.uuid)
)
files: list[tuple[str, Path]] = []
if (
verification_request.verification_type
== VerificationRequest.VerificationType.INDIVIDUAL
):
for ktp_path in verification_path.glob("ktp_*"):
files.append(("ktp", ktp_path))
break
elif (
verification_request.verification_type
== VerificationRequest.VerificationType.COMPANY
):
for field in ("ktp", "akta", "sk"):
for field_path in verification_path.glob(f"{field}_*"):
files.append((field, field_path))
break
if not files:
log.warning(
"No files found for verification request %s, sending message without media",
verification_request_id,
)
bot.send_message(chat_id, caption, reply_markup=markup, parse_mode="HTML")
return
media_group = []
for _, file_path in files:
file_obj = cast(telebot.types.InputFile, file_path.open("rb"))
media = telebot.types.InputMediaDocument(
file_obj,
)
media_group.append(media)
messages = bot.send_media_group(chat_id, media_group) assert len(messages) == len(files)
bot.reply_to(
messages[-1],
caption,
parse_mode="HTML",
reply_markup=markup,
)
username_display = f"@{username}" if username else chat_id
log.info(
"Sent verification request %s to provider %s",
verification_request_id,
username_display,
)
def send_all_pending_verifications_to_provider(
chat_id: int, username: str | None = None
) -> None:
pending_verifications = VerificationRequest.objects.filter(
status=VerificationRequest.Status.PENDING
)
count = pending_verifications.count()
if count == 0:
log.info("No pending verifications to send to provider")
return
log.info("Sending %s pending verification requests to provider", count)
for verification_request in pending_verifications:
try:
send_verification_request_to_provider(
chat_id=chat_id,
verification_request_id=verification_request.id,
username=username,
)
except Exception:
log.exception(
"Failed to send verification request %s to provider",
verification_request.id,
)
def notify_shaman_of_decision(
verification_request: VerificationRequest, is_update: bool = False
) -> None:
if verification_request.status == VerificationRequest.Status.PENDING:
return
status_display = (
"Approved"
if verification_request.status == VerificationRequest.Status.APPROVED
else "Declined"
)
update_prefix = "Updated: " if is_update else ""
title = f"{update_prefix}Verification Request {status_display}"
body = (
f"Your {verification_request.verification_type} verification request "
f"has been {status_display.lower()}."
)
notification = send_notification(
user=verification_request.requester,
title=title,
body=body,
)
if notification:
log.info(
"Sent notification to shaman %s about verification %s status: %s",
verification_request.requester_id,
verification_request.id,
status_display,
)