1+ from bson import ObjectId
12import logging
23from aio_pika .abc import AbstractIncomingMessage
4+ from pymongo .errors import PyMongoError
35from app .core .config import settings
46from app .api .sync_socket .router import send_message as send_sync_message
57from app .core .schemas import (
1113 UserBrief ,
1214 FriendRequestMessage ,
1315 SyncMessageType ,
16+ BrodcastMessage ,
17+ Message_Status ,
1418)
1519from app .core .db import (
1620 AsyncDatabase ,
2731logger = logging .getLogger (__name__ )
2832
2933
30- async def watch_user_updates ():
31- client = create_async_client ()
32- db = AsyncDatabase (client , settings .DATABASE_NAME )
34+ # async def watch_user_updates():
35+ # client = create_async_client()
36+ # db = AsyncDatabase(client, settings.DATABASE_NAME)
3337
34- async with db .user_profile .watch (
35- pipeline = [{"$match" : {"operationType" : "update" }}], full_document = "updateLookup"
36- ) as stream :
37- async for change in stream :
38- try :
39- # user_id = await db.user_profile.find_one({"_id": change['documentKey']['auth_id']})
40- cursor = db .friends .find ({"user_id" : change ["fullDocument" ]["auth_id" ]})
38+ # async with db.user_profile.watch(
39+ # pipeline=[{"$match": {"operationType": "update"}}], full_document="updateLookup"
40+ # ) as stream:
41+ # async for change in stream:
42+ # try:
43+ # # user_id = await db.user_profile.find_one({"_id": change['documentKey']['auth_id']})
44+ # cursor = db.friends.find({"user_id": change["fullDocument"]["auth_id"]})
4145
42- # Extracting the `friends_id` values from each result document
43- friend_ids = [doc ["friend_id" ] async for doc in cursor ]
46+ # # Extracting the `friends_id` values from each result document
47+ # friend_ids = [doc["friend_id"] async for doc in cursor]
4448
45- # Sending the data to the online frinds
46- data : dict [str :any ] = change ["updateDescription" ]["updatedFields" ]
47- data ["id" ] = change ["fullDocument" ]["auth_id" ]
49+ # # Sending the data to the online frinds
50+ # data: dict[str:any] = change["updateDescription"]["updatedFields"]
51+ # data["id"] = change["fullDocument"]["auth_id"]
4852
49- friend_update = FriendUpdateMessage .model_validate (data )
50- await send_sync_message (friend_ids , friend_update )
53+ # friend_update = FriendUpdateMessage.model_validate(data)
54+ # await send_sync_message(friend_ids, friend_update)
5155
52- # updating the lastupdate fo friends data
53- await db .friends .update_many (
54- {"friend_id" : change ["fullDocument" ]["auth_id" ]},
55- {"$set" : {"update_at" : change ["wallTime" ]}},
56- )
56+ # # updating the lastupdate fo friends data
57+ # await db.friends.update_many(
58+ # {"friend_id": change["fullDocument"]["auth_id"]},
59+ # {"$set": {"update_at": change["wallTime"]}},
60+ # )
5761
58- except Exception as e :
59- logger .error (f"Error processing user update : { e } " )
62+ # except Exception as e:
63+ # logger.error(f"Error processing user update : {e}")
6064
6165
6266@rabbit_consumer (
@@ -69,52 +73,100 @@ async def handle_online_status_update(
6973 await distribute_online_status_update (message = message , db = db )
7074
7175
72- async def watch_message_updates ():
73- """
74- Watches for updates in the MessageCollection and sends message status updates
75- to the sender when the message status changes.
76+ @rabbit_consumer (
77+ topic_name = settings .TOPICS .message_status_update .value ,
78+ exchange_name = settings .EXCHANGES .sync_message .value ,
79+ )
80+ async def process_message_status_updates (
81+ message : AbstractIncomingMessage , db : AsyncDatabase
82+ ):
7683 """
84+ Processes message status updates and forwards them to relevant senders.
7785
78- client = create_async_client ()
79- db = AsyncDatabase (client , settings .DATABASE_NAME )
86+ This function:
87+ 1. Decodes incoming message status updates
88+ 2. Queries affected messages from database
89+ 3. Groups messages by sender
90+ 4. Forwards status updates to each sender
8091
81- async with db .message .watch (
82- pipeline = [{"$match" : {"operationType" : "update" }}],
83- full_document = "updateLookup" ,
84- ) as stream :
85- async for change in stream :
86- try :
87- if not change ["updateDescription" ]["updatedFields" ]["status" ]:
92+ Args:
93+ message: Incoming RabbitMQ message containing status update payload
94+ db: Async database connection
95+ """
96+ try :
97+ try :
98+ decoded_data = message .body .decode ("utf-8" )
99+ payload : MessageStatusUpdate = MessageStatusUpdate .model_validate_json (
100+ decoded_data
101+ )
102+
103+ message_ids = [ObjectId (data .message_id ) for data in payload .data ]
104+
105+ except (UnicodeDecodeError , ValueError ) as e :
106+ logger .error (f"Failed to decode or validate message payload: { e } " )
107+ return
108+ state = (
109+ "received_time"
110+ if payload .status == Message_Status .recieved .value
111+ else "seen_time"
112+ )
113+
114+ try :
115+ cursor = db .message .find (
116+ {"_id" : {"$in" : message_ids }},
117+ projection = {"sender_id" : 1 , state : 1 },
118+ )
119+ except PyMongoError as e :
120+ logger .error (f"Database query failed: { e } " )
121+ return
122+
123+ sender_data : dict [str , list [dict [str , any ]]] = {}
124+ try :
125+ async for db_message in cursor :
126+ sender_id = db_message ["sender_id" ]
127+
128+ # Initialize sender list if not exists
129+ if sender_id not in sender_data :
130+ sender_data [sender_id ] = []
131+
132+ # Skip messages without timestamp
133+ if db_message [state ] is None :
134+ logger .warning (f"Message { db_message ['_id' ]} has null { state } " )
88135 continue
89136
90- message_id = change ["documentKey" ]["_id" ]
91- updated_field = change ["updateDescription" ]["updatedFields" ]
92- status = change ["updateDescription" ]["updatedFields" ]["status" ]
93-
94- # Get the timestamp of the update
95- timestamp = next (iter (updated_field .values ()))
96-
97- # Fetch the updated message from the database
98- message = Message .model_validate (change ["fullDocument" ])
99-
100- # Construct a MessageEvent Object
101- message_event = MessageEvent (
102- message_id = str (message_id ), timestamp = timestamp
137+ # Add message data to sender's list
138+ sender_data [sender_id ].append (
139+ {"timestamp" : db_message [state ], "message_id" : db_message ["_id" ]}
103140 )
104141
105- # Create a MessageStatusUpdate object to send to the user
106- sync_message = MessageStatusUpdate (
107- data = [message_event ],
108- status = status ,
109- )
142+ except PyMongoError as e :
143+ logger .error (f"Error iterating database cursor: { e } " )
144+ return
110145
111- # Send the status update to the message sender
146+ # send the data to the sender
147+ for sender_id , data in sender_data .items ():
148+ try :
149+ data = [
150+ MessageEvent (
151+ message_id = str (msg ["message_id" ]), timestamp = msg ["timestamp" ]
152+ )
153+ for msg in data
154+ ]
112155 await send_sync_message (
113- user_ids = [message .sender_id ], message_data = sync_message
156+ user_ids = [sender_id ],
157+ message_data = MessageStatusUpdate (data = data , status = payload .status ),
114158 )
115159
116160 except Exception as e :
117- print (f"Error processing user update(line:147) : { e } " )
161+ logger .error (f"Failed to send status update to sender { sender_id } : { e } " )
162+
163+ except Exception as e :
164+ logger .error (
165+ f"Unexpected error in process_message_status_updates: { e } " , exc_info = True
166+ )
167+ finally :
168+ # retry won't happen
169+ message .ack ()
118170
119171
120172@rabbit_consumer (
@@ -181,3 +233,17 @@ async def profile_media_update_confirmation(
181233 message : AbstractIncomingMessage ,
182234):
183235 await send_profilemedia_update_confirmation (data = message )
236+
237+
238+ @rabbit_consumer (
239+ topic_name = settings .TOPICS .chat_broadcast_selected .value ,
240+ exchange_name = settings .EXCHANGES .sync_message .value ,
241+ )
242+ async def send_message_to_users (
243+ message : AbstractIncomingMessage ,
244+ ):
245+ decoded_data = message .body .decode ("utf-8" )
246+ logger .info (f"{ decoded_data = } " )
247+ payload : BrodcastMessage = BrodcastMessage .model_validate_json (decoded_data )
248+
249+ await send_sync_message (user_ids = payload .ids , message_data = payload .data )
0 commit comments