Last commit not found
# App/Messages/Routes.py | |
from fastapi import APIRouter, HTTPException, Request | |
from .Model import Message | |
from .Schema import MessageCreate, MessageResponse, TransactionSchema | |
from uuid import UUID | |
from typing import List, Optional | |
import re | |
from datetime import datetime | |
from decimal import Decimal | |
from App.Payments.PaymentsRoutes import create_payment | |
from App.Payments.Schema import CreatePaymentRequest | |
from App.Users.Model import User | |
from App.Payments.Schema import PaymentMethod | |
from App.Payments.Model import Payment | |
from App.Plans.Model import Plan | |
from tortoise.contrib.pydantic import pydantic_model_creator | |
message_router = APIRouter(tags=["Messages"], prefix="/messages") | |
async def receive_message(message_data: MessageCreate): | |
message = Message.get_or_none(message_id=message_data.id) | |
Message_Pydantic = pydantic_model_creator(Message) | |
try: | |
# Extract data from the message content using regex | |
text = message_data.payload.message | |
parsed_data = parse_message_content(text) | |
# Process Mpesa Payments and prevent double entry from the sms gateway app (message_id must be unique) | |
if parsed_data and not message: | |
user: User = User.get_or_none(phoneNumber=parsed_data.phone_number) | |
# if the user sent an exact amount | |
data_plan: Plan = Plan.get_or_none( | |
amount=Decimal(parsed_data.amount_received) | |
) | |
payment_details = CreatePaymentRequest( | |
user_id=user.id, | |
plan_id=data_plan.id, | |
amount=Decimal(parsed_data.amount_received), | |
payment_method=PaymentMethod.MPESA, | |
transaction_id=parsed_data.transaction_id, | |
) | |
payment: Payment = await create_payment(payment_details) | |
await payment.create_subscription_or_balance() | |
# prevent double entries | |
if not message: | |
# Create a new message record with parsed_data | |
message = await Message.create( | |
device_id=message_data.deviceId, | |
event=message_data.event, | |
message_id=message_data.id, | |
webhook_id=message_data.webhookId, | |
message_content=text, | |
phone_number=message_data.payload.phoneNumber, | |
received_at=message_data.payload.receivedAt, | |
sim_number=message_data.payload.simNumber, | |
parsed_data=parsed_data, | |
) | |
print(message.__dict__) | |
if type(message) == Message: | |
data = await Message_Pydantic.from_tortoise_orm(message) | |
else: | |
data = await Message_Pydantic.from_queryset_single(message) | |
return data | |
except Exception as e: | |
raise HTTPException(status_code=400, detail=str(e)) | |
def parse_message_content(text: str) -> Optional[TransactionSchema]: | |
# Regular expression to capture the data from the message, allowing for different types of spaces | |
pattern = r"(\w+)\sConfirmed\.You have received Tsh([\d,]+\.\d{2}) from (\d{12}) - ([A-Z ]+) on (\d{1,2}/\d{1,2}/\d{2}) at ([\d:]+ [APM]+).*?balance\sis\sTsh([\d,]+\.\d{2})" | |
# Replace non-breaking spaces and other whitespace characters with regular spaces in the input text | |
text = re.sub(r"\s+", " ", text) | |
matches: TransactionSchema = re.search(pattern, text) | |
if matches: | |
data = { | |
"transaction_id": matches.group(1), | |
"amount_received": parse_decimal(matches.group(2)), | |
"phone_number": matches.group(3), | |
"name": matches.group(4).strip(), | |
"date": parse_date(matches.group(5), matches.group(6)), | |
"new_balance": parse_decimal(matches.group(7)), | |
} | |
return data | |
else: | |
# Return None if the message doesn't match the expected format | |
return None | |
def parse_decimal(amount_str: str) -> float: | |
# Remove commas and convert to Decimal | |
amount_str = amount_str.replace(",", "") | |
return float(Decimal(amount_str)) | |
def parse_date(date_str: str, time_str: str) -> str: | |
# Combine date and time strings and parse into ISO format | |
datetime_str = f"{date_str} {time_str}" | |
try: | |
dt = datetime.strptime(datetime_str, "%d/%m/%y %I:%M %p") | |
return dt.isoformat() | |
except ValueError: | |
return datetime_str # Return as-is if parsing fails | |