@@ -160,11 +160,7 @@ def send_message(
160160 )
161161
162162 try :
163- if self ._mode == self .MESSAGE_MODE :
164- api = self .service .users ().messages ()
165- else :
166- api = self .service .users ().threads ()
167- res = api .send (userId = 'me' , body = msg ).execute ()
163+ res = self .service .users ().messages ().send (userId = 'me' , body = msg ).execute ()
168164 return self ._build_message_from_ref (user_id , res , 'reference' )
169165
170166 except HttpError as error :
@@ -527,21 +523,34 @@ def get_messages(
527523 includeSpamTrash = include_spam_trash
528524 ).execute ()
529525
530- message_refs = []
531- if 'messages' in response : # ensure request was successful
532- message_refs .extend (response ['messages' ])
526+ if self ._mode == self .MESSAGE_MODE :
527+ message_refs = []
528+ if 'messages' in response : # ensure request was successful
529+ message_refs .extend (response ['messages' ])
530+ else :
531+ thread_refs = []
532+ if 'threads' in response : # ensure request was successful
533+ thread_refs .extend (response ['threads' ])
533534
534535 while 'nextPageToken' in response :
535536 page_token = response ['nextPageToken' ]
536- response = self . service . users (). messages () .list (
537+ response = api .list (
537538 userId = user_id ,
538539 q = query ,
539540 labelIds = labels_ids ,
540541 includeSpamTrash = include_spam_trash ,
541542 pageToken = page_token
542543 ).execute ()
543544
544- message_refs .extend (response ['messages' ])
545+ if self ._mode == self .MESSAGE_MODE :
546+ if 'messages' in response : # ensure request was successful
547+ message_refs .extend (response ['messages' ])
548+ else :
549+ if 'threads' in response : # ensure request was successful
550+ thread_refs .extend (response ['threads' ])
551+
552+ if self ._mode == self .THREAD_MODE :
553+ message_refs = self ._get_message_refs_from_thread_refs (user_id , thread_refs )
545554
546555 return self ._get_messages_from_refs (user_id , message_refs ,
547556 attachments )
@@ -583,6 +592,7 @@ def list_labels(self, user_id: str = 'me') -> List[Label]:
583592 labels = [Label (name = x ['name' ], id = x ['id' ]) for x in res ['labels' ]]
584593 return labels
585594
595+
586596 def _get_messages_from_refs (
587597 self ,
588598 user_id : str ,
@@ -686,11 +696,7 @@ def _build_message_from_ref(
686696
687697 try :
688698 # Get message JSON
689- if self ._mode == self .MESSAGE_MODE :
690- api = self .service .users ().messages ()
691- else :
692- api = self .service .users ().threads ()
693- message = api .get (
699+ message = self .service .users ().messages ().get (
694700 userId = user_id , id = message_ref ['id' ]
695701 ).execute ()
696702
@@ -812,11 +818,7 @@ def _evaluate_message_payload(
812818 if 'data' in payload ['body' ]:
813819 data = payload ['body' ]['data' ]
814820 else :
815- if self ._mode == self .MESSAGE_MODE :
816- api = self .service .users ().messages ()
817- else :
818- api = self .service .users ().threads ()
819- res = api .attachments ().get (
821+ res = self .service .users ().messages ().attachments ().get (
820822 userId = user_id , messageId = msg_id , id = att_id
821823 ).execute ()
822824 data = res ['data' ]
@@ -847,6 +849,115 @@ def _evaluate_message_payload(
847849
848850 return []
849851
852+ def _get_message_refs_from_thread_refs (
853+ self ,
854+ user_id : str ,
855+ thread_refs : List [dict ],
856+ parallel : bool = True
857+ ) -> List [Message ]:
858+ """
859+ Retrieves a list of message references from a list of thread references.
860+
861+ Args:
862+ user_id: The account the messages belong to.
863+ thread_refs: A list of thread references.
864+ parallel: Whether to retrieve messages in parallel. Default true.
865+ Currently parallelization is always on, since there is no
866+ reason to do otherwise.
867+
868+
869+ Returns:
870+ A list of Message objects.
871+
872+ Raises:
873+ googleapiclient.errors.HttpError: There was an error executing the
874+ HTTP request.
875+
876+ """
877+
878+ if not thread_refs :
879+ return []
880+
881+ if not parallel :
882+ message_refs = []
883+ for ref in thread_refs :
884+ message_refs .extend (self ._build_message_refs_from_thread_ref (user_id , ref ))
885+ return message_refs
886+
887+ max_num_threads = 12 # empirically chosen, prevents throttling
888+ target_msgs_per_thread = 10 # empirically chosen
889+ num_threads = min (
890+ math .ceil (len (thread_refs ) / target_msgs_per_thread ),
891+ max_num_threads
892+ )
893+ batch_size = math .ceil (len (thread_refs ) / num_threads )
894+ message_lists = [None ] * num_threads
895+
896+ def thread_download_batch (thread_num ):
897+ gmail = Gmail (_creds = self .creds )
898+
899+ start = thread_num * batch_size
900+ end = min (len (thread_refs ), (thread_num + 1 ) * batch_size )
901+ message_lists [thread_num ] = []
902+ for i in range (start , end ):
903+ message_lists [thread_num ].extend (gmail ._build_message_refs_from_thread_ref (
904+ user_id , thread_refs [i ]
905+ ))
906+ threads = [
907+ threading .Thread (target = thread_download_batch , args = (i ,))
908+ for i in range (num_threads )
909+ ]
910+
911+ for t in threads :
912+ t .start ()
913+
914+ for t in threads :
915+ t .join ()
916+
917+ return sum (message_lists , [])
918+
919+ def _build_message_refs_from_thread_ref (
920+ self ,
921+ user_id : str ,
922+ thread_ref : dict ,
923+ ) -> Message :
924+ """
925+ Creates a list of messages from a thread reference.
926+
927+ Args:
928+ user_id: The username of the account the message belongs to.
929+ thread_ref: A thread references returned from the Gmail
930+ API.
931+
932+ Returns:
933+ A list of dicts containing message IDs and thread IDs.
934+
935+ Raises:
936+ googleapiclient.errors.HttpError: There was an error executing the
937+ HTTP request.
938+
939+ """
940+
941+ try :
942+ # Get thread JSON
943+ thread = self .service .users ().threads ().get (
944+ userId = user_id , id = thread_ref ['id' ]
945+ ).execute ()
946+
947+ except HttpError as error :
948+ # Pass along the error
949+ raise error
950+
951+ else :
952+ messages = []
953+ for message in thread ['messages' ]:
954+ h = {
955+ "id" : message ['id' ],
956+ "threadId" : thread_ref ['id' ]
957+ }
958+ messages .append (h )
959+ return messages
960+
850961 def _create_message (
851962 self ,
852963 sender : str ,
0 commit comments