Skip to content

Commit 1c75395

Browse files
committed
Retrieve messages using thread API
1 parent 80f27b6 commit 1c75395

File tree

1 file changed

+131
-20
lines changed

1 file changed

+131
-20
lines changed

simplegmail/gmail.py

Lines changed: 131 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)