(20241126) Message-listing done.

This commit is contained in:
2024-11-26 12:59:54 +05:30
parent a10120f45b
commit c5699de003
2 changed files with 137 additions and 11 deletions
+136 -11
View File
@@ -60,11 +60,14 @@ import asyncio
import datetime import datetime
# For working with datatypes: # For working with datatypes:
from typing import Dict, Literal, List from typing import Dict, Literal, List, Any
# For debugging: # For debugging:
from icecream import IceCreamDebugger from icecream import IceCreamDebugger
# For computational help:
import math
# ***************************************************************************************************************** # *****************************************************************************************************************
# ***** **** # ***** ****
@@ -507,7 +510,7 @@ class AsyncGMailClient:
# ┛ ┗┗ ┛┛┗┻┗┫┗ ┛ # ┛ ┗┗ ┛┛┗┻┗┫┗ ┛
# ┛ # ┛
async def __list_messages_in_page( async def __list_messages_on_page(
self, self,
count: int = 100, count: int = 100,
query: str = None, query: str = None,
@@ -515,7 +518,25 @@ class AsyncGMailClient:
include_spam_and_trash: bool = False, include_spam_and_trash: bool = False,
next_page_token: str = None, next_page_token: str = None,
raise_exception: bool = False raise_exception: bool = False
): ) -> Dict[str, Any] | None:
"""
To enlist messages on one page. Google allows at most 500 results on one page. This method respects that
pagination limit and returns only what Google gives. This method should be used internally by the class and the
class should expose another method that calls this one in loop to get any arbitrary no. of messages as the user
desires.
DOCUMENTATION:
1. https://developers.google.com/gmail/api/reference/rest/v1/users.messages/list
2. https://developers.google.com/gmail/api/reference/rest/v1/users.messages#Message
:param count: The no. of messages to fetch.
:param query: Any query filter that is supported by GMail.
:param label_ids: The list of labels' ids that the mails must have on them.
:param include_spam_and_trash: Whether, or not, you would like to include mails categorized as spam and trash.
:param next_page_token: The token to fetch the next set of results.
:param raise_exception: If set to True, any exceptions that occur will be propagated, else they wil be
suppressed.
:return: The messages that matched the given conditions if the call was successful, else None.
"""
# Start by assuming failure: # Start by assuming failure:
page_messages = None page_messages = None
@@ -526,14 +547,21 @@ class AsyncGMailClient:
await self.__ensure_token() await self.__ensure_token()
# Build the needed params: # Build the needed params:
params_json = {"count": count} params_json = {
"maxResults": count,
"includeSpamTrash": include_spam_and_trash
}
if query: params_json["q"] = query if query: params_json["q"] = query
if next_page_token: params_json["pageToken"] = next_page_token if next_page_token: params_json["pageToken"] = next_page_token
if label_ids: params_json["labelIds"] = label_ids if isinstance(label_ids, list) else [label_ids] if label_ids: params_json["labelIds"] = label_ids if isinstance(label_ids, list) else [label_ids]
if include_spam_and_trash: params_json["includeSpamTrash"] = include_spam_and_trash
# Make the API call: # Make the API call:
if not self._debug_only_errors: self._printer("Listing All Labels.", self.__user_email) if not self._debug_only_errors: self._printer(
"Listing Messages for Page.",
self.__user_email,
count,
next_page_token
)
api_response = await self.__http_client.get( api_response = await self.__http_client.get(
url = f"https://gmail.googleapis.com/gmail/v1/users/{self.__user_email}/messages", url = f"https://gmail.googleapis.com/gmail/v1/users/{self.__user_email}/messages",
headers = {"Authorization": f"Bearer {self.__credentials.token}"}, headers = {"Authorization": f"Bearer {self.__credentials.token}"},
@@ -541,19 +569,114 @@ class AsyncGMailClient:
) )
# If the API call failed: # If the API call failed:
if api_response.status_code not in [200]: return labels if api_response.status_code not in [200]: return page_messages
# Else we format the response: # Else we format the response:
labels = {label.pop("name"): label for label in api_response.json().get("labels", [])} api_json = api_response.json()
page_messages = {
"messages": {m.pop("id"): m for m in api_json.get("messages", [])},
"nextPageToken": api_json.get("nextPageToken"),
"resultSizeEstimate": api_json["resultSizeEstimate"],
}
# In case something goes wrong along the way: # In case something goes wrong along the way:
except Exception as exception: except Exception as exception:
if raise_exception: raise if raise_exception: raise
self._printer(exception) self._printer(exception)
labels = None page_messages = None
# Done here: # Done here:
return labels return page_messages
async def list_messages(
self,
count: int = 100,
query: str = None,
label_ids: List[str] | str = None,
include_spam_and_trash: bool = False,
raise_exception: bool = False
) -> Dict[str, Any]:
"""
To enlist mail messages from a user's account.
DOCUMENTATION:
1. https://developers.google.com/gmail/api/reference/rest/v1/users.messages/list
2. https://developers.google.com/gmail/api/reference/rest/v1/users.messages#Message
:param count: The no. of messages to fetch.
:param query: Any query filter that is supported by GMail.
:param label_ids: The list of labels' ids that the mails must have on them.
:param include_spam_and_trash: Whether, or not, you would like to include mails categorized as spam and trash.
:param raise_exception: If set to True, any exceptions that occur will be propagated, else they wil be
suppressed.
:return: The messages that matched the given conditions if the call was successful, else None.
"""
# Start by assuming failure:
messages = None
try:
# Standard token-refresh check:
await self.__ensure_token()
# We convert the messages to a dict:
messages = {
"messages": {},
"nextPageToken": None,
"resultSizeEstimate": 0
}
# Let's figure out how many times we'll have to loop through the process to retrieve the target no. of
# messages. Google allows you to fetch info about at most 500 messages in one go.
max_per_call = 500 # ... because Google allows at most 500 entries in one call.
iterations_needed = int(math.ceil(count / max_per_call))
last_iteration_count = count - int((max_per_call * (iterations_needed - 1)))
# Run the loop those many times:
next_page_token = None
results_size_estimate = 0
for iteration_no in range(iterations_needed):
# Figure out the count for this page:
if iterations_needed > 1:
if iteration_no < (iterations_needed - 1): iteration_count = max_per_call
else: iteration_count = last_iteration_count
else: iteration_count = count
# Retrieve the messages for this page:
iteration_messages = await self.__list_messages_on_page(
count = iteration_count,
query = query,
label_ids = label_ids,
include_spam_and_trash = include_spam_and_trash,
next_page_token = next_page_token,
raise_exception = True
)
# Check if no data was received:
if iteration_messages is None: break
if not iteration_messages.get("messages"): break
# Now that we know that messages were received:
for k, v in iteration_messages["messages"].items(): messages["messages"][k] = v
results_size_estimate += iteration_messages["resultSizeEstimate"]
# If there is no next page after this, we break out of the loop:
next_page_token = iteration_messages["nextPageToken"]
if next_page_token is None: break
# Format the final response:
messages["nextPageToken"] = next_page_token
messages["resultSizeEstimate"] = results_size_estimate
# In case something goes wrong along the way:
except Exception as exception:
if raise_exception: raise
self._printer(exception)
messages = None
# Done here:
return messages
# ***************************************************************************************************************** # *****************************************************************************************************************
@@ -631,7 +754,9 @@ if __name__ == "__main__":
# label_background_color = "#7a2e0b" # label_background_color = "#7a2e0b"
# ))) # )))
print(json.to_string(await my_gmail.delete_label(label_id = "Label_6"))) # print(json.to_string(await my_gmail.delete_label(label_id = "Label_6")))
print(json.to_string(await my_gmail.list_messages(count = 10)))
asyncio.run(main()) asyncio.run(main())
+1
View File
@@ -307,6 +307,7 @@ class GoogleOAuth(OAuthBase):
credentials.expiry, credentials.expiry,
timezone = date_time.TIMEZONE_UTC timezone = date_time.TIMEZONE_UTC
) )
print("EXP. AT:", expires_at)
# Return the assessment based on the evaluation with the timezone considered: # Return the assessment based on the evaluation with the timezone considered:
return True if date_time.get_current_utc_date_time() >= expires_at else False return True if date_time.get_current_utc_date_time() >= expires_at else False