@@ -394,3 +394,183 @@ def update(self, messageId=None, roomId=None, text=None, markdown=None):
394394
395395 # Add edit() as an alias to the update() method for backward compatibility
396396 edit = update
397+
398+ def _is_direct_room (self , message ):
399+ """Determine if a message is from a direct (1:1) room.
400+
401+ Args:
402+ message: Message object with roomType property
403+
404+ Returns:
405+ bool: True if the message is from a direct room, False otherwise
406+ """
407+ if hasattr (message , "roomType" ):
408+ return message .roomType == "direct"
409+ return False
410+
411+ def _is_group_room (self , message ):
412+ """Determine if a message is from a group room (space).
413+
414+ Args:
415+ message: Message object with roomType property
416+
417+ Returns:
418+ bool: True if the message is from a group room, False otherwise
419+ """
420+ if hasattr (message , "roomType" ):
421+ return message .roomType == "group"
422+ return False
423+
424+ def get_thread_messages (self , message , max_scan = 500 ):
425+ """Retrieve all messages in a thread, including the root message.
426+
427+ This method provides a robust way to collect thread messages that works
428+ for both 1:1 conversations and spaces, handling the different permission
429+ models and API limitations.
430+
431+ Args:
432+ message: The message object to get the thread for
433+ max_scan (int): Maximum number of messages to scan when searching for parent
434+
435+ Returns:
436+ tuple: (thread_messages, root_message, error_message)
437+ - thread_messages: List of all messages in the thread (oldest to newest)
438+ - root_message: The root message of the thread (or None if not found)
439+ - error_message: Error description if any issues occurred
440+ """
441+ thread_messages = []
442+ root_message = None
443+ error_message = None
444+
445+ parent_id = getattr (message , "parentId" , None )
446+ room_id = getattr (message , "roomId" , None )
447+
448+ if not parent_id or not room_id :
449+ # Not a threaded message, return just this message
450+ return [message ], None , None
451+
452+ try :
453+ # Strategy 1: Try to get the parent message directly
454+ try :
455+ root_message = self .get (parent_id )
456+ thread_messages .append (root_message )
457+ except Exception :
458+ # Direct retrieval failed, try alternative strategies
459+ if self ._is_direct_room (message ):
460+ # For direct rooms, try list_direct with parentId
461+ try :
462+ direct_messages = list (
463+ self .list_direct (
464+ personId = getattr (message , "toPersonId" , None ),
465+ personEmail = getattr (
466+ message , "toPersonEmail" , None
467+ ),
468+ parentId = parent_id ,
469+ max = 100 ,
470+ )
471+ )
472+ if direct_messages :
473+ root_message = direct_messages [0 ]
474+ thread_messages .extend (direct_messages )
475+ except Exception :
476+ pass
477+ else :
478+ # For group rooms, try scanning recent messages
479+ try :
480+ scanned = 0
481+ for msg in self .list (roomId = room_id , max = 100 ):
482+ scanned += 1
483+ if getattr (msg , "id" , None ) == parent_id :
484+ root_message = msg
485+ thread_messages .append (msg )
486+ break
487+ if scanned >= max_scan :
488+ break
489+ except Exception :
490+ pass
491+
492+ if not root_message :
493+ error_message = f"Could not retrieve parent message { parent_id } . Bot may have joined after thread started or lacks permission."
494+
495+ # Strategy 2: Get all replies in the thread
496+ try :
497+ if self ._is_direct_room (message ):
498+ # For direct rooms, use list_direct
499+ replies = list (
500+ self .list_direct (
501+ personId = getattr (message , "toPersonId" , None ),
502+ personEmail = getattr (
503+ message , "toPersonEmail" , None
504+ ),
505+ parentId = parent_id ,
506+ max = 100 ,
507+ )
508+ )
509+ else :
510+ # For group rooms, use list
511+ replies = list (
512+ self .list (roomId = room_id , parentId = parent_id , max = 100 )
513+ )
514+
515+ # Add replies to thread messages, avoiding duplicates
516+ existing_ids = {
517+ getattr (m , "id" , None ) for m in thread_messages
518+ }
519+ for reply in replies :
520+ reply_id = getattr (reply , "id" , None )
521+ if reply_id and reply_id not in existing_ids :
522+ thread_messages .append (reply )
523+ existing_ids .add (reply_id )
524+
525+ except Exception as e :
526+ if not error_message :
527+ error_message = (
528+ f"Could not retrieve thread replies: { str (e )} "
529+ )
530+
531+ # Strategy 3: Ensure the original message is included
532+ original_id = getattr (message , "id" , None )
533+ if original_id and not any (
534+ getattr (m , "id" , None ) == original_id for m in thread_messages
535+ ):
536+ thread_messages .append (message )
537+
538+ # Sort messages by creation time (oldest to newest)
539+ thread_messages .sort (key = lambda m : getattr (m , "created" , "" ))
540+
541+ except Exception as e :
542+ error_message = f"Unexpected error retrieving thread: { str (e )} "
543+
544+ return thread_messages , root_message , error_message
545+
546+ def get_thread_context (self , message , max_scan = 500 ):
547+ """Get thread context including root message and all replies.
548+
549+ This is a convenience method that returns a structured result with
550+ thread information, making it easy to work with thread data.
551+
552+ Args:
553+ message: The message object to get thread context for
554+ max_scan (int): Maximum number of messages to scan when searching for parent
555+
556+ Returns:
557+ dict: Dictionary containing:
558+ - "thread_messages": List of all messages in thread (oldest to newest)
559+ - "root_message": The root message of the thread
560+ - "reply_count": Number of replies in the thread
561+ - "is_thread": Boolean indicating if this is a threaded conversation
562+ - "error": Error message if any issues occurred
563+ - "room_type": Type of room (direct/group)
564+ """
565+ thread_messages , root_message , error = self .get_thread_messages (
566+ message , max_scan
567+ )
568+
569+ return {
570+ "thread_messages" : thread_messages ,
571+ "root_message" : root_message ,
572+ "reply_count" : len (thread_messages ) - 1 if root_message else 0 ,
573+ "is_thread" : getattr (message , "parentId" , None ) is not None ,
574+ "error" : error ,
575+ "room_type" : getattr (message , "roomType" , "unknown" ),
576+ }
0 commit comments