File size: 4,461 Bytes
01a1238 427638d 01a1238 427638d 7d1b428 01a1238 fd01f78 9d7ad9e 7d1b428 01a1238 fd01f78 665318f fd01f78 665318f 01a1238 fd01f78 b1fe62a 7d1b428 d06f610 7d1b428 d06f610 427638d 01a1238 427638d c335f43 01a1238 427638d 01a1238 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# 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")
@message_router.post("/sms_received", response_model=MessageResponse)
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
|