narendrasinghd commited on
Commit
7adba0b
·
verified ·
1 Parent(s): 17142bd

Upload 18 files

Browse files
app/api/__pycache__/router.cpython-312.pyc ADDED
Binary file (15.3 kB). View file
 
app/api/router.py ADDED
@@ -0,0 +1,240 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from app.db import models
2
+ import os, json, random, requests
3
+ from sqlalchemy.orm import Session
4
+ from fastapi.responses import JSONResponse
5
+ from app.db.database import SessionLocal, engine
6
+ from app.db import crud
7
+ from app.services.realtor_service import RealtorService
8
+ from app.utils.log_utils import setup_logger
9
+ from fastapi import Request, Depends, APIRouter, HTTPException
10
+ from twilio.twiml.messaging_response import MessagingResponse
11
+ from app.services.properties_service import get_property_by_address_or_mls_number
12
+ from app.services.agent_service import verify_agent_by_agent_id_and_broker_name
13
+ from fastapi.responses import Response
14
+ from pydantic import BaseModel, Field
15
+ from twilio.rest import Client
16
+ from app.db.schemas import BuyerRealtorConfirmation, BuyerRealtorSignUP, CreatebookingRequest, GetBooking, ListingRealtorConfirmation, ListingRealtorSignUP, MessageRequestSignUP
17
+
18
+ from dotenv import load_dotenv
19
+ load_dotenv()
20
+
21
+ logger = setup_logger("router")
22
+
23
+ ACCOUNT_SID = os.getenv("ACCOUNT_SID")
24
+ AUTH_TOKEN = os.getenv("AUTH_TOKEN")
25
+ ai_agnet_number = os.getenv("AI_AGENT_NUMBER")
26
+ n8n_webhook_url = os.getenv("N8N_WEBHOOK_URL")
27
+
28
+ client = Client(ACCOUNT_SID, AUTH_TOKEN)
29
+
30
+ router = APIRouter()
31
+ models.Base.metadata.create_all(bind=engine)
32
+
33
+ def get_db():
34
+ db = SessionLocal()
35
+ try:
36
+ yield db
37
+ finally:
38
+ db.close()
39
+
40
+
41
+ @router.post('/realtor/get_properties')
42
+ async def get_properties(address: str=None, mls_number: str=None, db: Session = Depends(get_db)):
43
+ logger.warning(f"get_properties request: address: {address}, mls_number: {mls_number}")
44
+ response = get_property_by_address_or_mls_number(mls_number=mls_number, address=address)
45
+ return JSONResponse(content=response)
46
+
47
+ @router.post('/realtor/verify_agent')
48
+ async def verify_agent(agent_id: str, broker_name: str, db: Session = Depends(get_db)):
49
+ logger.warning(f"verify_agent request: agent_id: {agent_id}, broker_name: {broker_name}")
50
+ return verify_agent_by_agent_id_and_broker_name(agent_id=agent_id, broker_name=broker_name)
51
+
52
+ @router.post('/realtor/signup')
53
+ async def realtor_sign_up(request: MessageRequestSignUP, db: Session = Depends(get_db)):
54
+ logger.warning(f"singup request: {request}")
55
+ user = crud.create_user(db, full_name=request.full_name, phone_number=request.phone_number, type=request.type, agent_id=request.agent_id, broker_name=request.broker_name)
56
+ if not user:
57
+ raise HTTPException(status_code=400, detail="User creation failed")
58
+ return JSONResponse(content="user is signed up sucessfully.")
59
+
60
+
61
+ @router.post('/realtor/create_booking')
62
+ async def create_booking(request: CreatebookingRequest, db: Session = Depends(get_db)):
63
+ logger.warning(f"Create booking request: {request}")
64
+ try:
65
+ booking = crud.create_booking(db, buyer_agent_phone_number=request.buyer_agent_phone_number,
66
+ address=request.address, mls_number=request.mls_number, buyer_selected_date=request.buyer_selected_date,
67
+ buyer_selected_time=request.buyer_selected_time, listing_agent_phone_number=request.listing_agent_phone_number,
68
+ listing_agent_session_id=request.listing_agent_session_id, status=request.status)
69
+ except Exception as e:
70
+ db.rollback()
71
+ logger.error(f"Error in creating booking: {e}")
72
+ raise HTTPException(status_code=400, detail="booking creation failed")
73
+
74
+ return JSONResponse(content=booking)
75
+
76
+
77
+
78
+ @router.post('/realtor/book_showings')
79
+ async def buyer_book_showings(request: ListingRealtorSignUP, db: Session = Depends(get_db)):
80
+ logger.warning(f"Listing realtor singup request: {request}")
81
+ realtor_service = RealtorService()
82
+ response = realtor_service.book_showings(db, request.booking_address, request.mls_number, request.date, request.time, request.buyer_agent_phone_number)
83
+ logger.warning(f"Listing realtor sign up response: {response}")
84
+ return JSONResponse(content=response)
85
+
86
+ @router.post('/realtor/get_booking')
87
+ async def get_booking(request: GetBooking, db: Session = Depends(get_db)):
88
+ realtor_service = RealtorService()
89
+ response = realtor_service.get_booking(db, booking_id=request.booking_id, mls_number=request.mls_number, phone_number=request.phone_number)
90
+ logger.warning(f"Get booking response: {response}")
91
+ return JSONResponse(content=response)
92
+
93
+ @router.post('/buyer_realtor/signup')
94
+ async def buyer_realtor_sign_up(request: BuyerRealtorSignUP, db: Session = Depends(get_db)):
95
+ logger.warning(f"Buyer realtor singup request: {request}")
96
+ type = "buyer"
97
+ realtor_service = RealtorService()
98
+ response = realtor_service.buyer_realtor_sign_up(db, request.full_name, request.agent_id, request.broker_name, request.phone_number, type)
99
+ logger.warning(f"Buyer realtor sign up response: {response}")
100
+ return JSONResponse(content=response)
101
+
102
+ @router.post('/buyer_realtor/confirmation')
103
+ async def buyer_realtor_confirmation(request: BuyerRealtorConfirmation, db: Session = Depends(get_db)):
104
+ logger.warning(f"Buyer realtor confirmation request: {request}")
105
+ realtor_service = RealtorService()
106
+ response = realtor_service.buyer_realtor_confirmation(db, request.phone_number, request.booking_id, request.mls_number, request.date, request.time, request.confirmation)
107
+ logger.warning(f"Buyer realtor confirmation response: {response}")
108
+ return JSONResponse(content=response)
109
+
110
+ @router.post('/listing_realtor/confirmation')
111
+ async def listing_realtor_confirmation(request: ListingRealtorConfirmation, db: Session = Depends(get_db)):
112
+ logger.warning(f"Listing realtor confirmation request: {request}")
113
+ realtor_service = RealtorService()
114
+ response = realtor_service.listing_realtor_confirmation(db, request.session_id, request.date, request.time, request.confirmation)
115
+ logger.warning(f"Listing realtor confirmation response: {response}")
116
+ return JSONResponse(content=response)
117
+
118
+
119
+
120
+ # @router.post("/webhook/sms")
121
+ # async def receive_sms(request: Request):
122
+ # form_data = await request.form()
123
+ # logger.warning(f"Received SMS: {form_data}")
124
+ # sender = form_data.get("From")[9:]
125
+ # message_body = form_data.get("Body")
126
+
127
+ # logger.warning(f"Received SMS from {sender}: {message_body}")
128
+ # # # Reply to the sender
129
+ # # response = MessagingResponse()
130
+ # # response.message(f"Hello! You sent: {message_body}")
131
+
132
+ # # return str(response)
133
+
134
+
135
+ @router.get('/helth_check')
136
+ async def helth_check():
137
+ logger.warning(f"singup request: Comes")
138
+ return JSONResponse(content="Success")
139
+
140
+ @router.get('/')
141
+ async def test():
142
+ logger.warning(f"test request: Comes")
143
+ return JSONResponse(content="Go to /docs")
144
+
145
+
146
+ @router.post('/chatbot')
147
+ async def whatsapp_webhook(request: Request, db: Session = Depends(get_db)):
148
+ form_data = await request.form()
149
+ num_media = int(form_data.get('NumMedia', 0))
150
+ phone_number = form_data.get("From")[9:]
151
+ profile_name = form_data.get("ProfileName")
152
+ user_query = form_data.get("Body")
153
+ logger.warning(f"num_media: {num_media}, phone_number: {phone_number}, ProfileName: {profile_name}, user_query: {user_query}")
154
+
155
+ user = crud.get_user_by_phone_number(db, phone_number=phone_number)
156
+ if not user:
157
+ payload = {
158
+ "input_value": user_query,
159
+ "session_id": phone_number
160
+ }
161
+ logger.info(f"Payload: {payload}")
162
+ headers = {
163
+ 'Content-Type': 'application/json'
164
+ }
165
+ url = "https://workflows.kickcall.ai/api/v1/run/067c7617-4758-4f07-8f4e-457b4d10dbb6?stream=false&user_id=1"
166
+
167
+ try:
168
+ response = requests.post(url, headers=headers, data=json.dumps(payload))
169
+ logger.info(f"[Langflow][run] Response status code received: {response.status_code}")
170
+
171
+ if response.status_code == 200:
172
+ response_json = response.json()
173
+ logger.info(f"[Langflow][run] Response: {response_json}")
174
+ output_text = response_json["outputs"][0]["outputs"][0]["results"]["message"]["data"]["text"]
175
+
176
+ bot_resp = MessagingResponse()
177
+ msg = bot_resp.message()
178
+ msg.body(output_text)
179
+
180
+ return Response(content=str(bot_resp), media_type="application/xml")
181
+ else:
182
+ logger.info(f"Error: Received status code {response.status_code}")
183
+ bot_resp = MessagingResponse()
184
+ msg = bot_resp.message()
185
+ msg.body("I'm sorry, I couldn't process your request at the moment. Please try again later.")
186
+ return Response(content=str(bot_resp), media_type="application/xml")
187
+
188
+ except Exception as e:
189
+ logger.info(f"Error in processing user query: {e}")
190
+ bot_resp = MessagingResponse()
191
+ msg = bot_resp.message()
192
+ msg.body("I'm sorry, an unexpected error occurred. Please try again later.")
193
+ return Response(content=str(bot_resp), media_type="application/xml")
194
+
195
+ if user.type == "buyer":
196
+ payload = {
197
+ "input_value": user_query,
198
+ "session_id": phone_number
199
+ }
200
+ logger.info(f"Payload: {payload}")
201
+ headers = {
202
+ 'Content-Type': 'application/json'
203
+ }
204
+ url = "https://workflows.kickcall.ai/api/v1/run/c94b14f5-d7a7-4fc8-89d8-eb672716c778?stream=false&user_id=1"
205
+
206
+ try:
207
+ response = requests.post(url, headers=headers, data=json.dumps(payload))
208
+ logger.info(f"[Langflow][run] Response status code received: {response.status_code}")
209
+
210
+ if response.status_code == 200:
211
+ response_json = response.json()
212
+ logger.info(f"[Langflow][run] Response: {response_json}")
213
+ output_text = response_json["outputs"][0]["outputs"][0]["results"]["message"]["data"]["text"]
214
+
215
+ bot_resp = MessagingResponse()
216
+ msg = bot_resp.message()
217
+ msg.body(output_text)
218
+
219
+ return Response(content=str(bot_resp), media_type="application/xml")
220
+ else:
221
+ logger.info(f"Error: Received status code {response.status_code}")
222
+ bot_resp = MessagingResponse()
223
+ msg = bot_resp.message()
224
+ msg.body("I'm sorry, I couldn't process your request at the moment. Please try again later.")
225
+ return Response(content=str(bot_resp), media_type="application/xml")
226
+
227
+ except Exception as e:
228
+ logger.info(f"Error in processing user query: {e}")
229
+ bot_resp = MessagingResponse()
230
+ msg = bot_resp.message()
231
+ msg.body("I'm sorry, an unexpected error occurred. Please try again later.")
232
+ return Response(content=str(bot_resp), media_type="application/xml")
233
+
234
+ elif user.type == "listing":
235
+ logger.info(f"Message received by listing realto's ")
236
+ active_session_id = user.active_session_id
237
+
238
+ else:
239
+ logger.error(f"Message received by type : {user.type}")
240
+
app/db/__pycache__/crud.cpython-312.pyc ADDED
Binary file (5.23 kB). View file
 
app/db/__pycache__/database.cpython-312.pyc ADDED
Binary file (705 Bytes). View file
 
app/db/__pycache__/models.cpython-312.pyc ADDED
Binary file (2.42 kB). View file
 
app/db/__pycache__/schemas.cpython-312.pyc ADDED
Binary file (3.84 kB). View file
 
app/db/crud.py ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import uuid
2
+ from app.db import models
3
+ from sqlalchemy.orm import Session
4
+ from app.utils.log_utils import setup_logger
5
+
6
+ logger = setup_logger("crud")
7
+
8
+ def get_booking_by_mls_and_buyer_phone(db: Session, mls_number: str, buyer_agent_phone_number: str):
9
+ return db.query(models.booking).filter(
10
+ models.booking.mls_number == mls_number,
11
+ models.booking.buyer_agent_phone_number == buyer_agent_phone_number
12
+ ).first()
13
+
14
+ def get_user_by_phone_number(db: Session, phone_number: str):
15
+ return db.query(models.User).filter(models.User.phone_number == phone_number).first()
16
+
17
+ def create_user(db: Session, full_name: str, phone_number: str, type: str, agent_id=None, broker_name=None, active_session_id=None):
18
+ try:
19
+ new_user = models.User(full_name=full_name,
20
+ phone_number=phone_number,
21
+ type=type,
22
+ agent_id=agent_id,
23
+ broker_name=broker_name,
24
+ active_session_id=active_session_id)
25
+ db.add(new_user)
26
+ db.commit()
27
+ db.refresh(new_user)
28
+ return new_user
29
+ except Exception as e:
30
+ db.rollback()
31
+ logger.error(f"Error creating user: {e}")
32
+ return None
33
+
34
+ def get_booking_by_session_id(db: Session, session_id: str):
35
+ return db.query(models.booking).filter(models.booking.listing_agent_session_id == session_id).first()
36
+
37
+ def get_booking_by_id(db: Session, booking_id: int):
38
+ return db.query(models.booking).filter(models.booking.id == booking_id).first()
39
+
40
+ def get_booking_by_id_and_buyer_agent_phone_number(db: Session, booking_id: int, buyer_agent_phone_number: str):
41
+ return db.query(models.booking).filter(models.booking.id == booking_id,
42
+ models.booking.buyer_agent_phone_number == buyer_agent_phone_number).first()
43
+
44
+ def get_booking_by_mls_number_and_buyer_agent_phone_number(db: Session, mls_number: str, buyer_agent_phone_number: str):
45
+ return db.query(models.booking).filter(models.booking.mls_number == mls_number,
46
+ models.booking.buyer_agent_phone_number == buyer_agent_phone_number).first()
47
+
48
+ def create_booking(db: Session,
49
+ buyer_agent_phone_number: str,
50
+ address: str, mls_number: str,
51
+ buyer_selected_date: str,
52
+ buyer_selected_time: str,
53
+ listing_selected_date: str = None,
54
+ listing_selected_time: str = None,
55
+ listing_agent_phone_number: str = None,
56
+ listing_agent_session_id: str = None,
57
+ status: str = None):
58
+ try:
59
+ logger.info(f"Creating new booking with mls_number: {mls_number}")
60
+ new_booking = models.booking(buyer_agent_phone_number=buyer_agent_phone_number,
61
+ address=address,
62
+ mls_number=mls_number,
63
+ buyer_selected_date=buyer_selected_date,
64
+ buyer_selected_time=buyer_selected_time,
65
+ listing_selected_date=listing_selected_date,
66
+ listing_selected_time=listing_selected_time,
67
+ listing_agent_phone_number=listing_agent_phone_number,
68
+ listing_agent_session_id=listing_agent_session_id,
69
+ status=status)
70
+ db.add(new_booking)
71
+ db.commit()
72
+ db.refresh(new_booking)
73
+ return new_booking
74
+ except Exception as e:
75
+ db.rollback()
76
+ print(f"Error creating booking: {e}")
77
+ return None
app/db/database.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from dotenv import load_dotenv
3
+ from sqlalchemy import create_engine
4
+ from sqlalchemy.orm import sessionmaker
5
+ from sqlalchemy.ext.declarative import declarative_base
6
+
7
+ load_dotenv()
8
+
9
+ SQLALCHEMY_DATABASE_URL = os.getenv("POSTGRES_DATABASE_URL")
10
+
11
+ engine = create_engine(
12
+ SQLALCHEMY_DATABASE_URL,
13
+ pool_pre_ping=True
14
+ )
15
+ SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
16
+
17
+ Base = declarative_base()
app/db/models.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sqlalchemy import Column, Integer, JSON,ForeignKey, String, DateTime, func, ARRAY
2
+ from sqlalchemy.orm import relationship
3
+ from .database import Base
4
+
5
+ class User(Base):
6
+ __tablename__ = "users"
7
+
8
+ id = Column(Integer, primary_key=True, index=True)
9
+ phone_number = Column(String, unique=True, index=True)
10
+ full_name = Column(String, default="Unknown")
11
+ agent_id = Column(String, default="")
12
+ type = Column(String, default="buyer")
13
+ broker_name = Column(String, default="")
14
+ active_session_id = Column(String, default="")
15
+ queued_session_ids = Column(ARRAY(String), default=[])
16
+ completed_session_ids = Column(ARRAY(String), default=[])
17
+ created_at = Column(DateTime(timezone=True), server_default=func.now())
18
+
19
+ bookings = relationship("booking", back_populates="buyer_agent")
20
+
21
+ class booking(Base):
22
+ __tablename__ = "bookings"
23
+
24
+ id = Column(Integer, primary_key=True, index=True)
25
+ buyer_agent_phone_number = Column(String, ForeignKey("users.phone_number"), nullable=False)
26
+ address = Column(String, default="")
27
+ mls_number = Column(String, default="")
28
+ buyer_selected_date = Column(String, default="")
29
+ buyer_selected_time = Column(String, default="")
30
+ listing_selected_date = Column(String, default="")
31
+ listing_selected_time = Column(String, default="")
32
+ listing_agent_phone_number = Column(String, default="")
33
+ listing_agent_session_id = Column(String, default="")
34
+ status = Column(String, default="pending")
35
+ created_at = Column(DateTime(timezone=True), server_default=func.now())
36
+
37
+ buyer_agent = relationship("User", back_populates="bookings")
app/db/schemas.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel, Field
2
+
3
+ class MessageRequestSignUP(BaseModel):
4
+ phone_number: str = Field(default="+917389058485")
5
+ agent_id: str = Field(default="")
6
+ full_name: str = Field(default="agent_full_name")
7
+ broker_name: str = Field(default="")
8
+ type: str = Field(default="buyer")
9
+ listing_realtor_msg: str = Field(default="")
10
+
11
+
12
+ class CreatebookingRequest(BaseModel):
13
+ buyer_agent_phone_number: str = Field(default="+917389058485")
14
+ address: str = Field(default="booking_address_test")
15
+ mls_number: str = Field(default="mls_number_test")
16
+ buyer_selected_date: str = Field(default="date_test")
17
+ buyer_selected_time: str = Field(default="time_test")
18
+ listing_agent_phone_number: str = Field(default="")
19
+ listing_agent_session_id: str = Field(default="")
20
+ status: str = Field(default="pending")
21
+
22
+ class BuyerRealtorSignUP(BaseModel):
23
+ full_name: str = Field(default="")
24
+ agent_id: str = Field(default="")
25
+ broker_name: str = Field(default="")
26
+ phone_number: str = Field(default="")
27
+
28
+ class ListingRealtorSignUP(BaseModel):
29
+ booking_address: str = Field(default="")
30
+ mls_number: str = Field(default="")
31
+ date: str = Field(default="")
32
+ time: str = Field(default="")
33
+ buyer_agent_phone_number: str = Field(default="")
34
+
35
+ class BuyerRealtorConfirmation(BaseModel):
36
+ phone_number: str = Field(default="")
37
+ mls_number: str = Field(default="")
38
+ booking_id: str = Field(default="")
39
+ date: str = Field(default="")
40
+ time: str = Field(default="")
41
+ confirmation: str = Field(default="")
42
+
43
+ class ListingRealtorConfirmation(BaseModel):
44
+ confirmation: str = Field(default="")
45
+ date: str = Field(default="")
46
+ time: str = Field(default="")
47
+ session_id: str = Field(default="")
48
+
49
+ class GetBooking(BaseModel):
50
+ phone_number: str = Field(default="")
51
+ booking_id: str = Field(default="")
52
+ mls_number: str = Field(default="")
app/services/__pycache__/agent_service.cpython-312.pyc ADDED
Binary file (479 Bytes). View file
 
app/services/__pycache__/properties_service.cpython-312.pyc ADDED
Binary file (1.82 kB). View file
 
app/services/__pycache__/realtor_service.cpython-312.pyc ADDED
Binary file (18.2 kB). View file
 
app/services/agent_service.py ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+
2
+
3
+ def verify_agent_by_agent_id_and_broker_name(agent_id, broker_name):
4
+ if agent_id == "agent_id_test" and broker_name == "broker_name_test":
5
+ return True
6
+ elif agent_id == "king_agent" and broker_name == "king_broker":
7
+ return True
8
+ return False
app/services/properties_service.py ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from app.utils.log_utils import setup_logger
2
+
3
+ logger = setup_logger("properties_service")
4
+
5
+ def get_property_by_address_or_mls_number(mls_number, address):
6
+ if mls_number in ["123", "456", "789", "123456"]:
7
+ response = {
8
+ "address": "123 Main St, San Francisco, CA 94105",
9
+ "mls_number": mls_number,
10
+ "price": "$1,000,000",
11
+ "bedrooms": "3",
12
+ "bathrooms": "2",
13
+ "sqft": "1,500",
14
+ "lot_size": "0.25",
15
+ "year_built": "2000",
16
+ "description": "This is a beautiful 3 bedroom, 2 bathroom home with 1,500 sqft of living space on a 0.25 acre lot. The home was built in 2000 and is listed for $1,000,000.",
17
+ "agent_name": "Bob",
18
+ "agent_phone": "+917347256305"
19
+ }
20
+ elif mls_number == "654321":
21
+ response = {
22
+ "address": "456 Elm St, San Francisco, CA 94105",
23
+ "mls_number": mls_number,
24
+ "price": "$1,500,000",
25
+ "bedrooms": "4",
26
+ "bathrooms": "3",
27
+ "sqft": "2,000",
28
+ "lot_size": "0.5",
29
+ "year_built": "2010",
30
+ "description": "This is a beautiful 4 bedroom, 3 bathroom home with 2,000 sqft of living space on a 0.5 acre lot. The home was built in 2010 and is listed for $1,500,000.",
31
+ "agent_name": "Bob1",
32
+ "agent_phone": "+917347256302"
33
+ }
34
+ elif address == "321 Oak St, San Francisco, CA 94105":
35
+ response = {
36
+ "address": address,
37
+ "mls_number": "789012",
38
+ "price": "$2,000,000",
39
+ "bedrooms": "5",
40
+ "bathrooms": "4",
41
+ "sqft": "2,500",
42
+ "lot_size": "0.75",
43
+ "year_built": "2020",
44
+ "description": "This is a beautiful 5 bedroom, 4 bathroom home with 2,500 sqft of living space on a 0.75 acre lot. The home was built in 2020 and is listed for $2,000,000.",
45
+ "agent_name": "Bob2",
46
+ "agent_phone": "+917347256303"
47
+ }
48
+ else:
49
+ logger.error(f"Property not found for address: {address}, mls_number: {mls_number}")
50
+ response = None
51
+ return response
app/services/realtor_service.py ADDED
@@ -0,0 +1,264 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from app.db import crud
2
+ from app.services.agent_service import verify_agent_by_agent_id_and_broker_name
3
+ from app.services.properties_service import get_property_by_address_or_mls_number
4
+ from app.utils.log_utils import setup_logger
5
+ import requests, os, json, uuid
6
+ from dotenv import load_dotenv
7
+ load_dotenv()
8
+
9
+ ACCOUNT_SID = os.getenv("ACCOUNT_SID")
10
+ AUTH_TOKEN = os.getenv("AUTH_TOKEN")
11
+ ai_agnet_number = os.getenv("AI_AGENT_NUMBER")
12
+ n8n_webhook_url = os.getenv("N8N_WEBHOOK_URL")
13
+
14
+ logger = setup_logger("realtor_service")
15
+
16
+ def create_payload_for_whatsapp_message(from_number, to_number, body):
17
+ payload = [
18
+ {
19
+ "SmsMessageSid": "SM0b07afa5f1ede39427957b4dc127cab3",
20
+ "NumMedia": "0",
21
+ "ProfileName": "",
22
+ "MessageType": "text",
23
+ "SmsSid": "SM0b07afa5f1ede39427957b4dc127cab3",
24
+ "WaId": from_number,
25
+ "SmsStatus": "received",
26
+ "Body": f"paparaphrase this: {body}",
27
+ "To": f"whatsapp:{to_number}",
28
+ "NumSegments": "1",
29
+ "ReferralNumMedia": "0",
30
+ "MessageSid": "SM0b07afa5f1ede39427957b4dc127cab3",
31
+ "AccountSid": "",
32
+ "From": f"whatsapp:{from_number}",
33
+ "ApiVersion": "2010-04-01"
34
+ }
35
+ ]
36
+
37
+ try:
38
+
39
+ headers = {"Content-Type": "application/json"}
40
+ response = requests.post(n8n_webhook_url, headers=headers, data=json.dumps(payload))
41
+ logger.info(f"Response for whatsapp webhook: {response.text}")
42
+ except Exception as e:
43
+ logger.error(f"Error in sending message to agent: {e}")
44
+ return f"Error in sending message to agent: {str(e)}"
45
+ return "Message sent successfully"
46
+
47
+
48
+ class RealtorService:
49
+ def __init__(self):
50
+ pass
51
+
52
+ def book_showings(self, db, booking_address, mls_number, date, time, buyer_agent_phone_number):
53
+ buyer_selected_date = date
54
+ buyer_selected_time = time
55
+ logger.warning(f"Listing realtore - booking_address: {booking_address}, mls_number: {mls_number}, date: {buyer_selected_date}, time: {buyer_selected_time}, buyer_agent_phone_number: {buyer_agent_phone_number}")
56
+ #TODO: Add logic to get booking details from mls_number
57
+ response = get_property_by_address_or_mls_number(mls_number=mls_number, address=booking_address)
58
+ if response is None:
59
+ return "Invalid mls_number or address"
60
+ listing_agent_full_name = response["agent_name"]
61
+ listing_agent_phone_number = response["agent_phone"]
62
+ type = "listing"
63
+ booking_address = response["address"]
64
+ try:
65
+ buyer_user = crud.get_user_by_phone_number(db, phone_number=buyer_agent_phone_number)
66
+ if not buyer_user:
67
+ return "Buyer agent not found"
68
+
69
+ new_session_id = uuid.uuid4()
70
+ listing_user = crud.get_user_by_phone_number(db, phone_number=listing_agent_phone_number)
71
+ if listing_user:
72
+ if listing_user.active_session_id:
73
+ listing_user.queued_session_ids = listing_user.queued_session_ids + [new_session_id]
74
+ logger.info(f"The user already exists with the provided phone number and has an active session. Therefore, the task is being added to the queue: {buyer_agent_phone_number}")
75
+ db.commit()
76
+ db.refresh(listing_user)
77
+ booking = crud.get_booking_by_mls_number_and_buyer_agent_phone_number(db, mls_number=mls_number, buyer_agent_phone_number=buyer_agent_phone_number)
78
+ if booking:
79
+ booking.buyer_selected_date = buyer_selected_date
80
+ booking.buyer_selected_time = buyer_selected_time
81
+ db.commit()
82
+ db.refresh(booking)
83
+ return f"A booking already exists with this MLS number. The rescheduling request has been successfully processed. Your booking ID is {booking.id} for future reference."
84
+ else:
85
+ booking = crud.create_booking(db, buyer_agent_phone_number=buyer_agent_phone_number,
86
+ address=booking_address, mls_number=mls_number, buyer_selected_date=buyer_selected_date,
87
+ buyer_selected_time=buyer_selected_time, listing_agent_phone_number=listing_agent_phone_number,
88
+ listing_agent_session_id=new_session_id)
89
+ else:
90
+ logger.info(f"The user already exists but no active session found : {buyer_agent_phone_number}")
91
+ listing_user.active_session_id = new_session_id
92
+ db.commit()
93
+ db.refresh(listing_user)
94
+ booking = crud.get_booking_by_mls_number_and_buyer_agent_phone_number(db, mls_number=mls_number, buyer_agent_phone_number=buyer_agent_phone_number)
95
+ if booking:
96
+ booking.buyer_selected_date = buyer_selected_date
97
+ booking.buyer_selected_time = buyer_selected_time
98
+ db.commit()
99
+ db.refresh(booking)
100
+ return f"Rescheduling request has been successfully processed. Your booking ID is {booking.id}."
101
+ else:
102
+ booking = crud.create_booking(db, buyer_agent_phone_number=buyer_agent_phone_number,
103
+ address=booking_address, mls_number=mls_number, buyer_selected_date=buyer_selected_date,
104
+ buyer_selected_time=buyer_selected_time, listing_agent_phone_number=listing_agent_phone_number,
105
+ listing_agent_session_id=listing_user.active_session_id)
106
+ else:
107
+ listing_user = crud.create_user(db, full_name=listing_agent_full_name, phone_number=listing_agent_phone_number, type=type, active_session_id=new_session_id)
108
+ if not listing_user:
109
+ logger.info(f"Error in creating listing agent.")
110
+ return "Error in creating listing agent."
111
+ booking = crud.create_booking(db, buyer_agent_phone_number=buyer_agent_phone_number,
112
+ address=booking_address, mls_number=mls_number, buyer_selected_date=buyer_selected_date,
113
+ buyer_selected_time=buyer_selected_time, listing_agent_phone_number=listing_agent_phone_number,
114
+ listing_agent_session_id=new_session_id)
115
+
116
+ except Exception as e:
117
+ db.rollback()
118
+ logger.error(f"Error in creating booking: {e}")
119
+ return "booking creation failed"
120
+
121
+ if int(buyer_selected_time[:2])>12:
122
+ buyer_selected_time = f"{int(buyer_selected_time[:2])-12}{buyer_selected_time[2:]} PM"
123
+ else:
124
+ buyer_selected_time = f"{buyer_selected_time} AM"
125
+
126
+ listing_agent_body = f"Hello {listing_agent_full_name}, this is Alice assistant of {buyer_user.full_name}. We would like to schedule a showing for {booking_address} (MLS # {mls_number}) at {buyer_selected_time} on {buyer_selected_date}. The booking ID for this request is #{booking.id} for future reference. Can you make that work?."
127
+ logger.info(f"Sending message to listing agent from: {ai_agnet_number}, body: {listing_agent_body}, to: {listing_agent_phone_number}")
128
+ response = create_payload_for_whatsapp_message(from_number=listing_agent_phone_number, to_number=ai_agnet_number, body=listing_agent_body)
129
+ if response == "Message sent successfully":
130
+ return f"Do not say like its booked successfully say like: Your showing has been pending with the listing agent {listing_user.full_name}. I will notify you once the listing agent confirms the showing appointment. Your booking ID is #{booking.id} for future reference."
131
+ return "Error in sending message to listing agent"
132
+
133
+
134
+ def listing_realtor_confirmation(self, db, session_id, listing_selected_date, listing_selected_time, confirmation):
135
+ logger.warning(f"Listing realtore - listing_selected_date: {listing_selected_date}, listing_selected_time: {listing_selected_time}, session_id: {session_id}, confirmation: {confirmation}.")
136
+ try:
137
+ booking = crud.get_booking_by_session_id(db, session_id=session_id)
138
+ listing_user = crud.get_user_by_phone_number(db, phone_number=booking.listing_agent_phone_number)
139
+ is_listing_user_session_queued = False
140
+ except Exception as e:
141
+ logger.error(f"Error in creating user: {e}")
142
+ return f"booking not found with this session id, Error: {str(e)}"
143
+ if not booking:
144
+ return "booking not found with this session id"
145
+ booking.listing_selected_date = listing_selected_date
146
+ booking.listing_selected_time = listing_selected_time
147
+
148
+ if listing_selected_date == booking.buyer_selected_date and listing_selected_time == booking.buyer_selected_time:
149
+ logger.info(f"listing_selected_date and listing_selected_time are same as buyer_selected_date and buyer_selected_time")
150
+ confirmation = "confirmed"
151
+ if int(listing_selected_time[:2])>12:
152
+ listing_selected_time = f"{int(listing_selected_time[:2])-12}{listing_selected_time[2:]} PM"
153
+ else:
154
+ listing_selected_time = f"{listing_selected_time} AM"
155
+ if confirmation == "confirmed":
156
+ logger.warning(f"booking Schudule is confirmed by listing agent.")
157
+ booking.status = "confirmed"
158
+ if listing_user.queued_session_ids:
159
+ logger.info(f"listing user has queued sessions Found.")
160
+ listing_user.active_session_id = listing_user.queued_session_ids.pop(0)
161
+ is_listing_user_session_queued = True
162
+ listing_agent_body = f"Your showing at {booking.address} (MLS # {booking.mls_number}) is confirmed on {listing_selected_date} at {listing_selected_time} . I'll send you a reminder the day before to ensure you're prepared."
163
+ buyer_agent_body = f"Your showing at {booking.address} (MLS # {booking.mls_number}) is confirmed by {listing_user.full_name} on {listing_selected_date} at {listing_selected_time} . I'll send you a reminder the day before to ensure you're prepared."
164
+ elif confirmation == "cancelled":
165
+ logger.warning(f"booking Schudule is cancelled by listing agent.")
166
+ if listing_user.queued_session_ids:
167
+ logger.info(f"listing user has queued sessions Found.")
168
+ listing_user.active_session_id = listing_user.queued_session_ids.pop(0)
169
+ is_listing_user_session_queued = True
170
+ booking.status = "cancelled"
171
+ listing_agent_body = f"Thank you for letting us know. We will inform the buyer that the showing has been cancelled."
172
+ buyer_agent_body = f"Unfortunately, the listing agent has cancelled the showing. We apologize for any inconvenience caused."
173
+ elif confirmation == "rescheduled":
174
+ logger.warning(f"booking Schudule is rescheduled by listing agent. because of listing_selected_date: {listing_selected_date}, listing_selected_time: {listing_selected_time} and buyer_selected_date: {booking.buyer_selected_date}, buyer_selected_time: {booking.buyer_selected_time}")
175
+ booking.status = "rescheduled"
176
+ listing_agent_body = f"paparaphrase this: Your reschedule request for the showing has been submitted and is waiting for confirmation from the buyer agent. I’ll update you once they confirm the showing appointment. Your booking ID is #{booking.id} for reference."
177
+ buyer_agent_body = f"The listing agent for the booking at {booking.address} (MLS # {booking.mls_number}) wants to rescheduled the showing on {listing_selected_date} at {listing_selected_time} . Your booking ID is #{booking.id} for future reference, Please confirm if this new time works for you."
178
+ else:
179
+ logger.warning(f"Invalid confirmation status")
180
+ return "Invalid confirmation status"
181
+
182
+ logger.info(f"Sending message to listing agent from: {ai_agnet_number}, body: {buyer_agent_body}, to: {booking.buyer_agent_phone_number}")
183
+ response = create_payload_for_whatsapp_message(from_number=booking.buyer_agent_phone_number, to_number=ai_agnet_number, body=buyer_agent_body)
184
+ if response != "Message sent successfully":
185
+ logger.error(f"Error in sending message to listing agent")
186
+ return "Error in sending message to buyer agent"
187
+ db.commit()
188
+ return listing_agent_body
189
+
190
+ def buyer_realtor_confirmation(self, db, buyer_agent_phone_number, booking_id, mls_number, buyer_selected_date, buyer_selected_time, confirmation):
191
+ logger.warning(f"Buyer realtore - booking_id: {booking_id}, mls_number: {mls_number}, buyer_selected_date: {buyer_selected_date}, buyer_selected_time: {buyer_selected_time}, confirmation: {confirmation}.")
192
+ try:
193
+ if booking_id:
194
+ booking = crud.get_booking_by_id_and_buyer_agent_phone_number(db, booking_id=booking_id, buyer_agent_phone_number=buyer_agent_phone_number)
195
+ elif mls_number:
196
+ booking = crud.get_booking_by_mls_number_and_buyer_agent_phone_number(db, mls_number=mls_number, buyer_agent_phone_number=buyer_agent_phone_number)
197
+ else:
198
+ return "Invalid booking id or mls number"
199
+ except Exception as e:
200
+ logger.error(f"Error in creating user: {e}")
201
+ return f"booking not found with this booking id, Error: {str(e)}"
202
+ if not booking:
203
+ return "booking not found with this booking id"
204
+ booking.buyer_selected_date = buyer_selected_date
205
+ booking.buyer_selected_time = buyer_selected_time
206
+ if buyer_selected_date == booking.listing_selected_date and buyer_selected_time == booking.listing_selected_time:
207
+ logger.info(f"buyer_selected_date and buyer_selected_time are same as listing_selected_date and listing_selected_time")
208
+ confirmation = "confirmed"
209
+ if int(buyer_selected_time[:2])>12:
210
+ buyer_selected_time = f"{int(buyer_selected_time[:2])-12}{buyer_selected_time[2:]} PM"
211
+ else:
212
+ buyer_selected_time = f"{buyer_selected_time} AM"
213
+ if confirmation == "confirmed":
214
+ logger.warning(f"booking Schudule is confirmed by buyer agent.")
215
+ booking.status = "confirmed"
216
+ buyer_agent_body = f"Your showing at {booking.address} (MLS # {booking.mls_number}) is confirmed on {buyer_selected_date} at {buyer_selected_time}. Your booking ID is {booking.id} for future reference. I'll send you a reminder the day before to ensure you're prepared."
217
+ listing_agent_body = f"Your showing at {booking.address} (MLS # {booking.mls_number}) is confirmed on {buyer_selected_date} at {buyer_selected_time} . Your booking ID is {booking.id} for future reference. I'll send you a reminder the day before to ensure you're prepared."
218
+ elif confirmation == "cancelled":
219
+ logger.warning(f"booking Schudule is cancelled by buyer agent.")
220
+ booking.status = "cancelled"
221
+ buyer_agent_body = f"Thank you for letting us know. We will inform the listing Agent that the showing has been cancelled."
222
+ listing_agent_body = f"Unfortunately, the Buyer agent has cancelled the showing. We apologize for any inconvenience caused."
223
+ elif confirmation == "rescheduled":
224
+ logger.warning(f"booking Schudule is rescheduled by Buyer agent. because of listing_selected_date: {buyer_selected_date}, listing_selected_time: {buyer_selected_time} and buyer_selected_date: {booking.buyer_selected_date}, buyer_selected_time: {booking.buyer_selected_time}")
225
+ booking.status = "rescheduled"
226
+ buyer_agent_body = f"paparaphrase this: Your reschedule request for the showing has been submitted and is waiting for confirmation from the buyer agent. I’ll update you once they confirm the showing appointment. Your booking ID is #{booking.id} for reference."
227
+ listing_agent_body = f"The Buyer agent for the booking at {booking.address} (MLS # {booking.mls_number}) wants to rescheduled the showing on {buyer_selected_date} at {buyer_selected_time}. Your booking ID is #{booking.id} for future reference. Please confirm if this new time works for you."
228
+ else:
229
+ logger.warning(f"Invalid confirmation status")
230
+ return "Invalid confirmation status"
231
+
232
+ logger.info(f"Sending message to listing agent from: {ai_agnet_number}, body: {listing_agent_body}, to: {booking.listing_agent_phone_number}")
233
+ response = create_payload_for_whatsapp_message(from_number=booking.listing_agent_phone_number, to_number=ai_agnet_number, body=listing_agent_body)
234
+ if response != "Message sent successfully":
235
+ logger.error(f"Error in sending message to listing agent")
236
+ return "Error in sending message to Listing agent"
237
+ db.commit()
238
+ return buyer_agent_body
239
+
240
+
241
+ def buyer_realtor_sign_up(self, db, full_name, agent_id, broker_name, phone_number, type):
242
+ logger.warning(f"Buyer realtor singup request: {full_name}, {agent_id}, {broker_name}, {phone_number}, {type}")
243
+ #TODO: Add logic to verify agent_id and broker_name
244
+ if not verify_agent_by_agent_id_and_broker_name(agent_id=agent_id, broker_name=broker_name):
245
+ logger.warning(f"Invalid agent_id or broker_name")
246
+ return "Wrong agent id or broker name"
247
+ user = crud.create_user(db, full_name=full_name, phone_number=phone_number, type=type, agent_id=agent_id, broker_name=broker_name)
248
+ if not user:
249
+ "User creation failed"
250
+ return "user is signed up sucessfully. Now you can book the showing with the listing agent. would you like to book a showing now?"
251
+
252
+
253
+ def get_booking(self, db, booking_id, mls_number, phone_number):
254
+ logger.warning(f"Get booking request: {booking_id}, {mls_number}, {phone_number}")
255
+ if booking_id:
256
+ booking = crud.get_booking_by_id_and_buyer_agent_phone_number(db, booking_id=booking_id, buyer_agent_phone_number=phone_number)
257
+ elif mls_number:
258
+ booking = crud.get_booking_by_mls_number_and_buyer_agent_phone_number(db, mls_number=mls_number, buyer_agent_phone_number=phone_number)
259
+ else:
260
+ return "Invalid booking_id or mls_number or phone_number"
261
+ if not booking:
262
+ return "booking not found"
263
+ logger.info(f"booking details: {str(vars(booking))}")
264
+ return str(vars(booking))
app/utils/__pycache__/log_utils.cpython-312.pyc ADDED
Binary file (1.28 kB). View file
 
app/utils/log_utils.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ import colorlog
3
+
4
+ def setup_logger(logger_name: str, debug_color="white", info_color="green", propagate=False):
5
+ color_handler = colorlog.StreamHandler()
6
+ log_colors = {
7
+ "DEBUG": debug_color,
8
+ "INFO": info_color,
9
+ "WARNING": "yellow",
10
+ "ERROR": "red",
11
+ "CRITICAL": "bold_red",
12
+ }
13
+ color_handler.setFormatter(
14
+ colorlog.ColoredFormatter(
15
+ "%(log_color)s%(asctime)s [%(levelname)s] %(name)s: %(message)s",
16
+ datefmt="%Y-%m-%d %H:%M:%S",
17
+ log_colors=log_colors
18
+ )
19
+ )
20
+ logger = logging.getLogger(logger_name)
21
+ if logger.hasHandlers():
22
+ logger.handlers.clear()
23
+ logger.setLevel(logging.DEBUG)
24
+ logger.addHandler(color_handler)
25
+ logger.propagate = propagate
26
+ return logger