fast-gmail 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 aleti
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,322 @@
1
+ Metadata-Version: 2.1
2
+ Name: fast_gmail
3
+ Version: 1.0.0
4
+ Summary: GmailApi wrapper
5
+ Home-page: https://github.com/aleti1/fast_gmail
6
+ Author: Aleti
7
+ Author-email: aleti.open.source@gmail.com
8
+ License: MIT
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Programming Language :: Python
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: google-api-python-client==2.125.0
19
+ Requires-Dist: google-auth-httplib2==0.2.0
20
+ Requires-Dist: google-auth-oauthlib==1.2.0
21
+ Requires-Dist: typing-extensions==4.11.0
22
+
23
+ # Fast-gmail a simple python wrapper for gmailapi
24
+
25
+ [![PyPI Downloads](https://img.shields.io/pypi/dm/fast-gmail.svg?label=PyPI%20downloads)](
26
+ https://pypi.org/project/fast-gmail/)
27
+
28
+ ---
29
+ ## Functionalities:
30
+ - Send text/html messages
31
+ - Send messages with signature & attachments
32
+ - Get messages with pagination
33
+ - Filter messages
34
+ - Get drafts
35
+ - Edit message labels
36
+ - Get message attachments
37
+
38
+
39
+ ## Instalation:
40
+ `pip install fast-gmail`
41
+
42
+ ---
43
+
44
+ ## Usage:
45
+
46
+ ### Initialization
47
+ - Go to https://console.developers.google.com/apis/credentials and login to correct account
48
+ - Setup Oauth consent screen
49
+ - Create new credentials with OAuth client ID for web or installed app
50
+ - Set Authorized Javascript origins to http://localhost:3000/
51
+ - Set Authorized redirect URIs to http://localhost:3000/
52
+ - Download the credentials.json file
53
+ - Go to Enable APIs & services and enable GmailAPI
54
+
55
+ With credentials.json file downloaded we can connect to Gmail
56
+ ```
57
+ gmail = GmailApi(
58
+ credentials_file_path = __path_to_credentials_json_file__,
59
+ port = 3000, # default is 3000 but you can use any other port IMPORTANT: if you change this default value don't forget to also changed on Authorized Javascript origins & Authorized redirect URIs
60
+ )
61
+ ```
62
+
63
+ ### Send message
64
+ ```
65
+ from fast_gmail import GmailApi
66
+
67
+ messages = GmailApi().send_message(
68
+ sender: str,
69
+ to: str,
70
+ subject: str,
71
+ html: Optional[str]=None,
72
+ text: Optional[str]=None,
73
+ cc: Optional[List[str] | str]=None,
74
+ bcc: Optional[List[str] | str]=None,
75
+ attachments: Optional[List[str] | str]=None,
76
+ signature: bool = True
77
+ )
78
+ """Sends an email message.
79
+ Args:
80
+ sender (str): Email address of the message sender.
81
+ to (str): Email address of the recipient.
82
+ subject (str): Subject line of the email.
83
+ html (Optional[str], optional): HTML content of the email body. Defaults to None.
84
+ text (Optional[str], optional): Plain text content of the email body. Defaults to None.
85
+ cc (Optional[List[str] | str], optional): List of email addresses to carbon copy. Defaults to None.
86
+ bcc (Optional[List[str] | str], optional): List of email addresses to blind carbon copy. Defaults to None.
87
+ attachments (Optional[List[str] | str], optional): List of file paths to attach to the email. Defaults to None.
88
+ signature (bool, optional): Whether to include a signature (if configured). Defaults to True.
89
+ Returns:
90
+ Optional[Message]: An object representing the sent message, or None on error.
91
+ """
92
+ ```
93
+
94
+ ### Get messages
95
+ ```
96
+ messages = GmailApi().get_messages(
97
+ includeSpamTrash: bool = False,
98
+ max_results: int = 10,
99
+ labels: Union[List[Labels], List[str], None] = None,
100
+ next_page_token: str = None,
101
+ existing_pages: Optional[List[str] | str] = None,
102
+ previous_page_token: Optional[str] = None,
103
+ query: Union[SearchParams, str] = None,
104
+ )
105
+ """Retrieves messages from a user's mailbox.
106
+ Args:
107
+ includeSpamTrash (bool, optional): If True, includes messages from spam and trash folders. Defaults to False.
108
+ max_results (int, optional): Maximum number of messages to return. Defaults to MAX_RESULTS (10).
109
+ labels (Union[List[Labels], List[str], None], optional): List of label IDs or strings to filter by. Defaults to None (all labels).
110
+ next_page_token (str, optional): Page token for fetching the next page of results.
111
+ existing_pages (Optional[List[str] | str], optional): Used internally for pagination. Defaults to None.
112
+ previous_page_token (Optional[str], optional): Page token for fetching the previous page of results.
113
+ query (Union[SearchParams, str], optional): Search query to filter messages. Can be a string or a SearchParams object.
114
+ Returns:
115
+ GetMessagesResponse: An object containing the retrieved messages and pagination information.
116
+ """
117
+ ```
118
+
119
+ ### Get INBOX/SPAM/TRASH messages
120
+ ```
121
+ messages = GmailApi().get_inbox_messages(
122
+ max_results: int = MAX_RESULTS,
123
+ next_page_token: str = None,
124
+ existing_pages: Optional[List[str] | str] = None,
125
+ previous_page_token: Optional[str] = None,
126
+ query: Union[SearchParams, str] = None
127
+ )
128
+
129
+ # helpers for spam or trash
130
+ messages = GmailApi().get_spam_messages()
131
+ messages = GmailApi().get_trash_messages()
132
+ ```
133
+
134
+ ### GET message
135
+ ```
136
+ message = GmailApi().get_message(id)
137
+ # returns a Message object
138
+ ```
139
+
140
+ ### GET attachment
141
+ ```
142
+ message = GmailApi().get_message(id)
143
+ attachment_id = None
144
+ attachment_name = None
145
+ for attacment in message.attachments:
146
+ attachment_id = attachment.part_id
147
+ attachment_name = attachment.filename
148
+
149
+ # get attachment by id
150
+ by_id = message.get_attachment(id=attachment_id)
151
+ # or by filename
152
+ by_name = message.get_attachment(filename=attachment_name) # returns an Attachment object
153
+
154
+ # save file to disk
155
+ by_id.save(filepath=f"/tmp/{attachment.filename}", overwrite=True)
156
+ ```
157
+
158
+ ### Message actions
159
+ ```
160
+ message = GmailApi().get_message(id)
161
+
162
+ # mark read or unread
163
+ message.toggle_read_unread
164
+ # or
165
+ message.mark_as_read
166
+ # or
167
+ message.mark_as_unread
168
+
169
+ # spam
170
+ message.toggle_spam
171
+ # or
172
+ message.mark_spam
173
+ message.mark_not_spam
174
+
175
+ # trash
176
+ message.toggle_trash
177
+ # or
178
+ message.move_to_trash
179
+ message.move_from_trash
180
+
181
+ # starred
182
+ message.toggle_starred
183
+ message.mark_starred
184
+ message.mark_not_starred
185
+
186
+ #important
187
+ message.toggle_important
188
+ message.mark_important
189
+ message.mark_not_important
190
+
191
+ # other properties
192
+ message.body
193
+ message.html
194
+ message.plain
195
+ message.recipient
196
+ message.subject
197
+ message.sender
198
+ message.snippet
199
+ message.is_starred
200
+ message.is_important
201
+ message.is_spam
202
+ message.is_unread
203
+ ...
204
+
205
+ ```
206
+
207
+ ---
208
+
209
+ ## Classes:
210
+
211
+ ### class GmailApi:
212
+ ###### Attributes:
213
+
214
+ - **credentials (Optional[Credentials])**: Stores user credentials (OAuth tokens) for accessing Gmail.
215
+ - **google_service (GoogleService)**: An instance of GoogleService used for making API requests.
216
+ - **max_results (int)**: Maximum number of messages to retrieve per request (default 10).
217
+ - **SEPARATOR_SYMBOL (str)**: String used for separating page tokens during pagination.
218
+ - **profile_data (Optional[GmailProfile])**: Caches the user's Gmail profile information.
219
+
220
+ ###### Constructor (__init__):
221
+ - Initializes the GmailApi object.
222
+ - Reads credentials from token and credentials files.
223
+ - Handles refreshing expired tokens.
224
+ - Creates a GoogleService instance for making API calls.
225
+
226
+ ###### Methods:
227
+ ###### Message Retrieval:
228
+ - **get_message(id: str)-> Optional[Message]**: Retrieves a message by its ID.
229
+ - **get_draft(id: str)-> Optional[Message]**: Retrieves a draft by its ID.
230
+ ###### Message Sending:
231
+ - **send_message(sender, to, subject, ...)-> Optional[Message]**: Sends an email message with various options (text, HTML, attachments, signature).
232
+ ###### Inbox Management:
233
+ - **get_inbox_messages()-> GetMessagesResponse**: Retrieves messages from the inbox.
234
+ Similar methods exist for getting trash, spam, and all messages with various filtering options.
235
+ ###### Drafts Management:
236
+ - **get_draft_messages()**: Retrieves draft messages.
237
+ ###### Attachment Management:
238
+ - **get_attachment(message_id: str, attachment_id: str) -> Optional[MessagePartBody]**: Retrieves an attachment from a message.
239
+ ###### Profile and Label Management:
240
+ - **labels**: Returns a list of user labels.
241
+ - **profile**: Retrieves and caches the user's Gmail profile information.
242
+
243
+ Overall, this code provides a well-structured and functional interface for interacting with Gmail using the Gmail API.
244
+
245
+ ---
246
+
247
+ ### class Message:
248
+ ###### Properties:
249
+ - **id**: Unique identifier for the message.
250
+ - **snippet**: Shortened summary of the message content.
251
+ - **threadId**: Thread ID that groups related messages together.
252
+ - **historyId**: Unique identifier for a specific message history change.
253
+ - **sizeEstimate**: Approximate size of the message in bytes.
254
+ - **internalDate**: Internal timestamp representing when the message was created.
255
+ - **payload**: Object of type MessagePart containing the parsed content of the message body and attachments.
256
+ - **raw**: Optional property containing the raw email data (if available).
257
+ - **labelIds**: List of label IDs associated with the message.
258
+ - **labels**: List of GmailLabel objects representing the message's labels (fetched on demand).
259
+
260
+ ###### Additional Message Properties:
261
+ - **message_headers**: Returns a list of MessageHeader objects from the message's payload (if available).
262
+ - **body**: Returns the message body content (combines plain text and HTML if both exist).
263
+ - **html**: Returns the HTML content of the message body (if available).
264
+ - **plain**: Returns the plain text content of the message body (if available).
265
+ - **alternative**: Returns the text content of the most appropriate alternative part (if message uses multipart/alternative).
266
+ - **recipient**: Extracts the recipient's email address from the "To" header (if available).
267
+ - **message_id**: Extracts the message ID from the message headers (if available).
268
+ - **subject**: Extracts the subject line from the message headers (if available).
269
+ - **sender**: Extracts the sender's email address from the "From" header (if available).
270
+ - **has_attachments**: Checks if the message or its nested parts contain attachments.
271
+ - **attachments**: Returns a list of Attachment objects for all attachments within the message.
272
+ - **is_unread, is_starred, is_important, etc.**: Boolean properties indicating the message's label status (unread, starred, important, spam, draft, trash).
273
+ - **created_date**: Converts the internalDate to a datetime object representing the message creation time.
274
+ - **date**: Parses the "Received" or "Date" header to get the message delivery date (if available).
275
+ - **date_string**: Formats the delivery date according to a specified format string (defaults to "%a, %d %b %Y %H:%M:%S %z").
276
+
277
+ ---
278
+
279
+ ###### Methods:
280
+ - **mark_as_read, mark_as_unread**: Toggles the unread flag for the message.
281
+ - **toogle_read_unread**: Shortcut to toggle the unread flag based on the current state.
282
+ - **mark_spam, mark_not_spam**: Toggles the spam label for the message.
283
+ - **toggle_spam**: Shortcut to toggle the spam label based on the current state.
284
+ - **move_to_trash, move_from_trash**: Toggles the trash label for the message.
285
+ - **toggle_trash**: Shortcut to toggle the trash label based on the current state.
286
+ - **mark_important, mark_not_important**: Toggles the important label for the message.
287
+ - **toggle_important**: Shortcut to toggle the important label based on the current state.
288
+ - **mark_starred, mark_not_starred**: Toggles the starred label for the message.
289
+ - **toggle_starred**: Shortcut to toggle the starred label based on the current state.
290
+ - **get_attachment(filename=None, id=None)**: Retrieves an attachment by filename or internal part ID.
291
+ - **get_labels**: Fetches and returns a list of GmailLabel objects for all labels associated with the message.
292
+ - **add_label(label), add_labels(labels)**: Adds labels to the message.
293
+ - **remove_label(label), remove_labels(labels)**: Removes labels from the message.
294
+ - **modify_labels(labels_to_add=None, labels_to_remove=None)**: Updates message labels by adding and/or removing labels.
295
+
296
+ Overall, this Message class provides a comprehensive way to access and manage individual email messages within a Gmail account. It allows you to retrieve message details, content, attachments, labels, and perform various actions like marking as read/unread, starring, labeling, and trash management.
297
+
298
+ ---
299
+
300
+ ### class Attachment:
301
+ ###### Properties:
302
+ - **filename (str)**: This attribute stores the filename of the attachment.
303
+ - **mimeType (str)**: This attribute stores the MIME type of the attachment content, which specifies the format of the data (e.g., image/jpeg, text/plain).
304
+ - **part_id (str)**: This attribute stores the internal identifier for the attachment within the message parts. This ID is used to download the attachment using the Gmail API.
305
+ - **google_service (GoogleService)**: This attribute holds a reference to the GoogleService object used for downloading the attachment. This object likely encapsulates authentication and communication details with the Gmail API.
306
+ - **id (Optional[str])**: This optional attribute stores the ID of the attachment generated by Gmail after a message is fetched using the .get() method. This ID might change for the same attachment across different fetches.
307
+ - **data (Optional[str])**: This optional attribute stores the Base64 encoded content of the attachment data downloaded from the API.
308
+
309
+ ###### Methods:
310
+ - **download() -> None**: This method downloads the attachment data from the Gmail API if it hasn't been downloaded already. It raises exceptions for errors like missing message ID or HTTP errors during download.
311
+ - **save(filepath: Optional[str] = None, overwrite: bool = False) -> None**: This method saves the attachment data to a file. It allows specifying the filepath and whether to overwrite existing files. It raises an error if the file already exists and overwrite is set to False.
312
+
313
+ Overall, the Attachment class provides functionality to manage attachments associated with Gmail messages. It handles downloading attachments from the API, storing downloaded data, and saving them to local files.
314
+
315
+ ---
316
+
317
+ ### class GetMessagesResponse:
318
+ ###### Attributes:
319
+ - **next_page_token (Optional[str], optional)**: Token for fetching the next page of results. Defaults to None.
320
+ - **existing_pages (Optional[str], optional)**: Used internally for pagination. Defaults to None. List like string of page tokens separated by GmailApi.SEPARATOR_SYMBOL
321
+ - **previous_page_token (Optional[List[str | None]], optional)**: Token for fetching the previous page of results (may be empty list). Defaults to [].
322
+ - **messages (List[Message])**: List of retrieved message objects.
@@ -0,0 +1,300 @@
1
+ # Fast-gmail a simple python wrapper for gmailapi
2
+
3
+ [![PyPI Downloads](https://img.shields.io/pypi/dm/fast-gmail.svg?label=PyPI%20downloads)](
4
+ https://pypi.org/project/fast-gmail/)
5
+
6
+ ---
7
+ ## Functionalities:
8
+ - Send text/html messages
9
+ - Send messages with signature & attachments
10
+ - Get messages with pagination
11
+ - Filter messages
12
+ - Get drafts
13
+ - Edit message labels
14
+ - Get message attachments
15
+
16
+
17
+ ## Instalation:
18
+ `pip install fast-gmail`
19
+
20
+ ---
21
+
22
+ ## Usage:
23
+
24
+ ### Initialization
25
+ - Go to https://console.developers.google.com/apis/credentials and login to correct account
26
+ - Setup Oauth consent screen
27
+ - Create new credentials with OAuth client ID for web or installed app
28
+ - Set Authorized Javascript origins to http://localhost:3000/
29
+ - Set Authorized redirect URIs to http://localhost:3000/
30
+ - Download the credentials.json file
31
+ - Go to Enable APIs & services and enable GmailAPI
32
+
33
+ With credentials.json file downloaded we can connect to Gmail
34
+ ```
35
+ gmail = GmailApi(
36
+ credentials_file_path = __path_to_credentials_json_file__,
37
+ port = 3000, # default is 3000 but you can use any other port IMPORTANT: if you change this default value don't forget to also changed on Authorized Javascript origins & Authorized redirect URIs
38
+ )
39
+ ```
40
+
41
+ ### Send message
42
+ ```
43
+ from fast_gmail import GmailApi
44
+
45
+ messages = GmailApi().send_message(
46
+ sender: str,
47
+ to: str,
48
+ subject: str,
49
+ html: Optional[str]=None,
50
+ text: Optional[str]=None,
51
+ cc: Optional[List[str] | str]=None,
52
+ bcc: Optional[List[str] | str]=None,
53
+ attachments: Optional[List[str] | str]=None,
54
+ signature: bool = True
55
+ )
56
+ """Sends an email message.
57
+ Args:
58
+ sender (str): Email address of the message sender.
59
+ to (str): Email address of the recipient.
60
+ subject (str): Subject line of the email.
61
+ html (Optional[str], optional): HTML content of the email body. Defaults to None.
62
+ text (Optional[str], optional): Plain text content of the email body. Defaults to None.
63
+ cc (Optional[List[str] | str], optional): List of email addresses to carbon copy. Defaults to None.
64
+ bcc (Optional[List[str] | str], optional): List of email addresses to blind carbon copy. Defaults to None.
65
+ attachments (Optional[List[str] | str], optional): List of file paths to attach to the email. Defaults to None.
66
+ signature (bool, optional): Whether to include a signature (if configured). Defaults to True.
67
+ Returns:
68
+ Optional[Message]: An object representing the sent message, or None on error.
69
+ """
70
+ ```
71
+
72
+ ### Get messages
73
+ ```
74
+ messages = GmailApi().get_messages(
75
+ includeSpamTrash: bool = False,
76
+ max_results: int = 10,
77
+ labels: Union[List[Labels], List[str], None] = None,
78
+ next_page_token: str = None,
79
+ existing_pages: Optional[List[str] | str] = None,
80
+ previous_page_token: Optional[str] = None,
81
+ query: Union[SearchParams, str] = None,
82
+ )
83
+ """Retrieves messages from a user's mailbox.
84
+ Args:
85
+ includeSpamTrash (bool, optional): If True, includes messages from spam and trash folders. Defaults to False.
86
+ max_results (int, optional): Maximum number of messages to return. Defaults to MAX_RESULTS (10).
87
+ labels (Union[List[Labels], List[str], None], optional): List of label IDs or strings to filter by. Defaults to None (all labels).
88
+ next_page_token (str, optional): Page token for fetching the next page of results.
89
+ existing_pages (Optional[List[str] | str], optional): Used internally for pagination. Defaults to None.
90
+ previous_page_token (Optional[str], optional): Page token for fetching the previous page of results.
91
+ query (Union[SearchParams, str], optional): Search query to filter messages. Can be a string or a SearchParams object.
92
+ Returns:
93
+ GetMessagesResponse: An object containing the retrieved messages and pagination information.
94
+ """
95
+ ```
96
+
97
+ ### Get INBOX/SPAM/TRASH messages
98
+ ```
99
+ messages = GmailApi().get_inbox_messages(
100
+ max_results: int = MAX_RESULTS,
101
+ next_page_token: str = None,
102
+ existing_pages: Optional[List[str] | str] = None,
103
+ previous_page_token: Optional[str] = None,
104
+ query: Union[SearchParams, str] = None
105
+ )
106
+
107
+ # helpers for spam or trash
108
+ messages = GmailApi().get_spam_messages()
109
+ messages = GmailApi().get_trash_messages()
110
+ ```
111
+
112
+ ### GET message
113
+ ```
114
+ message = GmailApi().get_message(id)
115
+ # returns a Message object
116
+ ```
117
+
118
+ ### GET attachment
119
+ ```
120
+ message = GmailApi().get_message(id)
121
+ attachment_id = None
122
+ attachment_name = None
123
+ for attacment in message.attachments:
124
+ attachment_id = attachment.part_id
125
+ attachment_name = attachment.filename
126
+
127
+ # get attachment by id
128
+ by_id = message.get_attachment(id=attachment_id)
129
+ # or by filename
130
+ by_name = message.get_attachment(filename=attachment_name) # returns an Attachment object
131
+
132
+ # save file to disk
133
+ by_id.save(filepath=f"/tmp/{attachment.filename}", overwrite=True)
134
+ ```
135
+
136
+ ### Message actions
137
+ ```
138
+ message = GmailApi().get_message(id)
139
+
140
+ # mark read or unread
141
+ message.toggle_read_unread
142
+ # or
143
+ message.mark_as_read
144
+ # or
145
+ message.mark_as_unread
146
+
147
+ # spam
148
+ message.toggle_spam
149
+ # or
150
+ message.mark_spam
151
+ message.mark_not_spam
152
+
153
+ # trash
154
+ message.toggle_trash
155
+ # or
156
+ message.move_to_trash
157
+ message.move_from_trash
158
+
159
+ # starred
160
+ message.toggle_starred
161
+ message.mark_starred
162
+ message.mark_not_starred
163
+
164
+ #important
165
+ message.toggle_important
166
+ message.mark_important
167
+ message.mark_not_important
168
+
169
+ # other properties
170
+ message.body
171
+ message.html
172
+ message.plain
173
+ message.recipient
174
+ message.subject
175
+ message.sender
176
+ message.snippet
177
+ message.is_starred
178
+ message.is_important
179
+ message.is_spam
180
+ message.is_unread
181
+ ...
182
+
183
+ ```
184
+
185
+ ---
186
+
187
+ ## Classes:
188
+
189
+ ### class GmailApi:
190
+ ###### Attributes:
191
+
192
+ - **credentials (Optional[Credentials])**: Stores user credentials (OAuth tokens) for accessing Gmail.
193
+ - **google_service (GoogleService)**: An instance of GoogleService used for making API requests.
194
+ - **max_results (int)**: Maximum number of messages to retrieve per request (default 10).
195
+ - **SEPARATOR_SYMBOL (str)**: String used for separating page tokens during pagination.
196
+ - **profile_data (Optional[GmailProfile])**: Caches the user's Gmail profile information.
197
+
198
+ ###### Constructor (__init__):
199
+ - Initializes the GmailApi object.
200
+ - Reads credentials from token and credentials files.
201
+ - Handles refreshing expired tokens.
202
+ - Creates a GoogleService instance for making API calls.
203
+
204
+ ###### Methods:
205
+ ###### Message Retrieval:
206
+ - **get_message(id: str)-> Optional[Message]**: Retrieves a message by its ID.
207
+ - **get_draft(id: str)-> Optional[Message]**: Retrieves a draft by its ID.
208
+ ###### Message Sending:
209
+ - **send_message(sender, to, subject, ...)-> Optional[Message]**: Sends an email message with various options (text, HTML, attachments, signature).
210
+ ###### Inbox Management:
211
+ - **get_inbox_messages()-> GetMessagesResponse**: Retrieves messages from the inbox.
212
+ Similar methods exist for getting trash, spam, and all messages with various filtering options.
213
+ ###### Drafts Management:
214
+ - **get_draft_messages()**: Retrieves draft messages.
215
+ ###### Attachment Management:
216
+ - **get_attachment(message_id: str, attachment_id: str) -> Optional[MessagePartBody]**: Retrieves an attachment from a message.
217
+ ###### Profile and Label Management:
218
+ - **labels**: Returns a list of user labels.
219
+ - **profile**: Retrieves and caches the user's Gmail profile information.
220
+
221
+ Overall, this code provides a well-structured and functional interface for interacting with Gmail using the Gmail API.
222
+
223
+ ---
224
+
225
+ ### class Message:
226
+ ###### Properties:
227
+ - **id**: Unique identifier for the message.
228
+ - **snippet**: Shortened summary of the message content.
229
+ - **threadId**: Thread ID that groups related messages together.
230
+ - **historyId**: Unique identifier for a specific message history change.
231
+ - **sizeEstimate**: Approximate size of the message in bytes.
232
+ - **internalDate**: Internal timestamp representing when the message was created.
233
+ - **payload**: Object of type MessagePart containing the parsed content of the message body and attachments.
234
+ - **raw**: Optional property containing the raw email data (if available).
235
+ - **labelIds**: List of label IDs associated with the message.
236
+ - **labels**: List of GmailLabel objects representing the message's labels (fetched on demand).
237
+
238
+ ###### Additional Message Properties:
239
+ - **message_headers**: Returns a list of MessageHeader objects from the message's payload (if available).
240
+ - **body**: Returns the message body content (combines plain text and HTML if both exist).
241
+ - **html**: Returns the HTML content of the message body (if available).
242
+ - **plain**: Returns the plain text content of the message body (if available).
243
+ - **alternative**: Returns the text content of the most appropriate alternative part (if message uses multipart/alternative).
244
+ - **recipient**: Extracts the recipient's email address from the "To" header (if available).
245
+ - **message_id**: Extracts the message ID from the message headers (if available).
246
+ - **subject**: Extracts the subject line from the message headers (if available).
247
+ - **sender**: Extracts the sender's email address from the "From" header (if available).
248
+ - **has_attachments**: Checks if the message or its nested parts contain attachments.
249
+ - **attachments**: Returns a list of Attachment objects for all attachments within the message.
250
+ - **is_unread, is_starred, is_important, etc.**: Boolean properties indicating the message's label status (unread, starred, important, spam, draft, trash).
251
+ - **created_date**: Converts the internalDate to a datetime object representing the message creation time.
252
+ - **date**: Parses the "Received" or "Date" header to get the message delivery date (if available).
253
+ - **date_string**: Formats the delivery date according to a specified format string (defaults to "%a, %d %b %Y %H:%M:%S %z").
254
+
255
+ ---
256
+
257
+ ###### Methods:
258
+ - **mark_as_read, mark_as_unread**: Toggles the unread flag for the message.
259
+ - **toogle_read_unread**: Shortcut to toggle the unread flag based on the current state.
260
+ - **mark_spam, mark_not_spam**: Toggles the spam label for the message.
261
+ - **toggle_spam**: Shortcut to toggle the spam label based on the current state.
262
+ - **move_to_trash, move_from_trash**: Toggles the trash label for the message.
263
+ - **toggle_trash**: Shortcut to toggle the trash label based on the current state.
264
+ - **mark_important, mark_not_important**: Toggles the important label for the message.
265
+ - **toggle_important**: Shortcut to toggle the important label based on the current state.
266
+ - **mark_starred, mark_not_starred**: Toggles the starred label for the message.
267
+ - **toggle_starred**: Shortcut to toggle the starred label based on the current state.
268
+ - **get_attachment(filename=None, id=None)**: Retrieves an attachment by filename or internal part ID.
269
+ - **get_labels**: Fetches and returns a list of GmailLabel objects for all labels associated with the message.
270
+ - **add_label(label), add_labels(labels)**: Adds labels to the message.
271
+ - **remove_label(label), remove_labels(labels)**: Removes labels from the message.
272
+ - **modify_labels(labels_to_add=None, labels_to_remove=None)**: Updates message labels by adding and/or removing labels.
273
+
274
+ Overall, this Message class provides a comprehensive way to access and manage individual email messages within a Gmail account. It allows you to retrieve message details, content, attachments, labels, and perform various actions like marking as read/unread, starring, labeling, and trash management.
275
+
276
+ ---
277
+
278
+ ### class Attachment:
279
+ ###### Properties:
280
+ - **filename (str)**: This attribute stores the filename of the attachment.
281
+ - **mimeType (str)**: This attribute stores the MIME type of the attachment content, which specifies the format of the data (e.g., image/jpeg, text/plain).
282
+ - **part_id (str)**: This attribute stores the internal identifier for the attachment within the message parts. This ID is used to download the attachment using the Gmail API.
283
+ - **google_service (GoogleService)**: This attribute holds a reference to the GoogleService object used for downloading the attachment. This object likely encapsulates authentication and communication details with the Gmail API.
284
+ - **id (Optional[str])**: This optional attribute stores the ID of the attachment generated by Gmail after a message is fetched using the .get() method. This ID might change for the same attachment across different fetches.
285
+ - **data (Optional[str])**: This optional attribute stores the Base64 encoded content of the attachment data downloaded from the API.
286
+
287
+ ###### Methods:
288
+ - **download() -> None**: This method downloads the attachment data from the Gmail API if it hasn't been downloaded already. It raises exceptions for errors like missing message ID or HTTP errors during download.
289
+ - **save(filepath: Optional[str] = None, overwrite: bool = False) -> None**: This method saves the attachment data to a file. It allows specifying the filepath and whether to overwrite existing files. It raises an error if the file already exists and overwrite is set to False.
290
+
291
+ Overall, the Attachment class provides functionality to manage attachments associated with Gmail messages. It handles downloading attachments from the API, storing downloaded data, and saving them to local files.
292
+
293
+ ---
294
+
295
+ ### class GetMessagesResponse:
296
+ ###### Attributes:
297
+ - **next_page_token (Optional[str], optional)**: Token for fetching the next page of results. Defaults to None.
298
+ - **existing_pages (Optional[str], optional)**: Used internally for pagination. Defaults to None. List like string of page tokens separated by GmailApi.SEPARATOR_SYMBOL
299
+ - **previous_page_token (Optional[List[str | None]], optional)**: Token for fetching the previous page of results (may be empty list). Defaults to [].
300
+ - **messages (List[Message])**: List of retrieved message objects.
@@ -0,0 +1,322 @@
1
+ Metadata-Version: 2.1
2
+ Name: fast_gmail
3
+ Version: 1.0.0
4
+ Summary: GmailApi wrapper
5
+ Home-page: https://github.com/aleti1/fast_gmail
6
+ Author: Aleti
7
+ Author-email: aleti.open.source@gmail.com
8
+ License: MIT
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Programming Language :: Python
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: google-api-python-client==2.125.0
19
+ Requires-Dist: google-auth-httplib2==0.2.0
20
+ Requires-Dist: google-auth-oauthlib==1.2.0
21
+ Requires-Dist: typing-extensions==4.11.0
22
+
23
+ # Fast-gmail a simple python wrapper for gmailapi
24
+
25
+ [![PyPI Downloads](https://img.shields.io/pypi/dm/fast-gmail.svg?label=PyPI%20downloads)](
26
+ https://pypi.org/project/fast-gmail/)
27
+
28
+ ---
29
+ ## Functionalities:
30
+ - Send text/html messages
31
+ - Send messages with signature & attachments
32
+ - Get messages with pagination
33
+ - Filter messages
34
+ - Get drafts
35
+ - Edit message labels
36
+ - Get message attachments
37
+
38
+
39
+ ## Instalation:
40
+ `pip install fast-gmail`
41
+
42
+ ---
43
+
44
+ ## Usage:
45
+
46
+ ### Initialization
47
+ - Go to https://console.developers.google.com/apis/credentials and login to correct account
48
+ - Setup Oauth consent screen
49
+ - Create new credentials with OAuth client ID for web or installed app
50
+ - Set Authorized Javascript origins to http://localhost:3000/
51
+ - Set Authorized redirect URIs to http://localhost:3000/
52
+ - Download the credentials.json file
53
+ - Go to Enable APIs & services and enable GmailAPI
54
+
55
+ With credentials.json file downloaded we can connect to Gmail
56
+ ```
57
+ gmail = GmailApi(
58
+ credentials_file_path = __path_to_credentials_json_file__,
59
+ port = 3000, # default is 3000 but you can use any other port IMPORTANT: if you change this default value don't forget to also changed on Authorized Javascript origins & Authorized redirect URIs
60
+ )
61
+ ```
62
+
63
+ ### Send message
64
+ ```
65
+ from fast_gmail import GmailApi
66
+
67
+ messages = GmailApi().send_message(
68
+ sender: str,
69
+ to: str,
70
+ subject: str,
71
+ html: Optional[str]=None,
72
+ text: Optional[str]=None,
73
+ cc: Optional[List[str] | str]=None,
74
+ bcc: Optional[List[str] | str]=None,
75
+ attachments: Optional[List[str] | str]=None,
76
+ signature: bool = True
77
+ )
78
+ """Sends an email message.
79
+ Args:
80
+ sender (str): Email address of the message sender.
81
+ to (str): Email address of the recipient.
82
+ subject (str): Subject line of the email.
83
+ html (Optional[str], optional): HTML content of the email body. Defaults to None.
84
+ text (Optional[str], optional): Plain text content of the email body. Defaults to None.
85
+ cc (Optional[List[str] | str], optional): List of email addresses to carbon copy. Defaults to None.
86
+ bcc (Optional[List[str] | str], optional): List of email addresses to blind carbon copy. Defaults to None.
87
+ attachments (Optional[List[str] | str], optional): List of file paths to attach to the email. Defaults to None.
88
+ signature (bool, optional): Whether to include a signature (if configured). Defaults to True.
89
+ Returns:
90
+ Optional[Message]: An object representing the sent message, or None on error.
91
+ """
92
+ ```
93
+
94
+ ### Get messages
95
+ ```
96
+ messages = GmailApi().get_messages(
97
+ includeSpamTrash: bool = False,
98
+ max_results: int = 10,
99
+ labels: Union[List[Labels], List[str], None] = None,
100
+ next_page_token: str = None,
101
+ existing_pages: Optional[List[str] | str] = None,
102
+ previous_page_token: Optional[str] = None,
103
+ query: Union[SearchParams, str] = None,
104
+ )
105
+ """Retrieves messages from a user's mailbox.
106
+ Args:
107
+ includeSpamTrash (bool, optional): If True, includes messages from spam and trash folders. Defaults to False.
108
+ max_results (int, optional): Maximum number of messages to return. Defaults to MAX_RESULTS (10).
109
+ labels (Union[List[Labels], List[str], None], optional): List of label IDs or strings to filter by. Defaults to None (all labels).
110
+ next_page_token (str, optional): Page token for fetching the next page of results.
111
+ existing_pages (Optional[List[str] | str], optional): Used internally for pagination. Defaults to None.
112
+ previous_page_token (Optional[str], optional): Page token for fetching the previous page of results.
113
+ query (Union[SearchParams, str], optional): Search query to filter messages. Can be a string or a SearchParams object.
114
+ Returns:
115
+ GetMessagesResponse: An object containing the retrieved messages and pagination information.
116
+ """
117
+ ```
118
+
119
+ ### Get INBOX/SPAM/TRASH messages
120
+ ```
121
+ messages = GmailApi().get_inbox_messages(
122
+ max_results: int = MAX_RESULTS,
123
+ next_page_token: str = None,
124
+ existing_pages: Optional[List[str] | str] = None,
125
+ previous_page_token: Optional[str] = None,
126
+ query: Union[SearchParams, str] = None
127
+ )
128
+
129
+ # helpers for spam or trash
130
+ messages = GmailApi().get_spam_messages()
131
+ messages = GmailApi().get_trash_messages()
132
+ ```
133
+
134
+ ### GET message
135
+ ```
136
+ message = GmailApi().get_message(id)
137
+ # returns a Message object
138
+ ```
139
+
140
+ ### GET attachment
141
+ ```
142
+ message = GmailApi().get_message(id)
143
+ attachment_id = None
144
+ attachment_name = None
145
+ for attacment in message.attachments:
146
+ attachment_id = attachment.part_id
147
+ attachment_name = attachment.filename
148
+
149
+ # get attachment by id
150
+ by_id = message.get_attachment(id=attachment_id)
151
+ # or by filename
152
+ by_name = message.get_attachment(filename=attachment_name) # returns an Attachment object
153
+
154
+ # save file to disk
155
+ by_id.save(filepath=f"/tmp/{attachment.filename}", overwrite=True)
156
+ ```
157
+
158
+ ### Message actions
159
+ ```
160
+ message = GmailApi().get_message(id)
161
+
162
+ # mark read or unread
163
+ message.toggle_read_unread
164
+ # or
165
+ message.mark_as_read
166
+ # or
167
+ message.mark_as_unread
168
+
169
+ # spam
170
+ message.toggle_spam
171
+ # or
172
+ message.mark_spam
173
+ message.mark_not_spam
174
+
175
+ # trash
176
+ message.toggle_trash
177
+ # or
178
+ message.move_to_trash
179
+ message.move_from_trash
180
+
181
+ # starred
182
+ message.toggle_starred
183
+ message.mark_starred
184
+ message.mark_not_starred
185
+
186
+ #important
187
+ message.toggle_important
188
+ message.mark_important
189
+ message.mark_not_important
190
+
191
+ # other properties
192
+ message.body
193
+ message.html
194
+ message.plain
195
+ message.recipient
196
+ message.subject
197
+ message.sender
198
+ message.snippet
199
+ message.is_starred
200
+ message.is_important
201
+ message.is_spam
202
+ message.is_unread
203
+ ...
204
+
205
+ ```
206
+
207
+ ---
208
+
209
+ ## Classes:
210
+
211
+ ### class GmailApi:
212
+ ###### Attributes:
213
+
214
+ - **credentials (Optional[Credentials])**: Stores user credentials (OAuth tokens) for accessing Gmail.
215
+ - **google_service (GoogleService)**: An instance of GoogleService used for making API requests.
216
+ - **max_results (int)**: Maximum number of messages to retrieve per request (default 10).
217
+ - **SEPARATOR_SYMBOL (str)**: String used for separating page tokens during pagination.
218
+ - **profile_data (Optional[GmailProfile])**: Caches the user's Gmail profile information.
219
+
220
+ ###### Constructor (__init__):
221
+ - Initializes the GmailApi object.
222
+ - Reads credentials from token and credentials files.
223
+ - Handles refreshing expired tokens.
224
+ - Creates a GoogleService instance for making API calls.
225
+
226
+ ###### Methods:
227
+ ###### Message Retrieval:
228
+ - **get_message(id: str)-> Optional[Message]**: Retrieves a message by its ID.
229
+ - **get_draft(id: str)-> Optional[Message]**: Retrieves a draft by its ID.
230
+ ###### Message Sending:
231
+ - **send_message(sender, to, subject, ...)-> Optional[Message]**: Sends an email message with various options (text, HTML, attachments, signature).
232
+ ###### Inbox Management:
233
+ - **get_inbox_messages()-> GetMessagesResponse**: Retrieves messages from the inbox.
234
+ Similar methods exist for getting trash, spam, and all messages with various filtering options.
235
+ ###### Drafts Management:
236
+ - **get_draft_messages()**: Retrieves draft messages.
237
+ ###### Attachment Management:
238
+ - **get_attachment(message_id: str, attachment_id: str) -> Optional[MessagePartBody]**: Retrieves an attachment from a message.
239
+ ###### Profile and Label Management:
240
+ - **labels**: Returns a list of user labels.
241
+ - **profile**: Retrieves and caches the user's Gmail profile information.
242
+
243
+ Overall, this code provides a well-structured and functional interface for interacting with Gmail using the Gmail API.
244
+
245
+ ---
246
+
247
+ ### class Message:
248
+ ###### Properties:
249
+ - **id**: Unique identifier for the message.
250
+ - **snippet**: Shortened summary of the message content.
251
+ - **threadId**: Thread ID that groups related messages together.
252
+ - **historyId**: Unique identifier for a specific message history change.
253
+ - **sizeEstimate**: Approximate size of the message in bytes.
254
+ - **internalDate**: Internal timestamp representing when the message was created.
255
+ - **payload**: Object of type MessagePart containing the parsed content of the message body and attachments.
256
+ - **raw**: Optional property containing the raw email data (if available).
257
+ - **labelIds**: List of label IDs associated with the message.
258
+ - **labels**: List of GmailLabel objects representing the message's labels (fetched on demand).
259
+
260
+ ###### Additional Message Properties:
261
+ - **message_headers**: Returns a list of MessageHeader objects from the message's payload (if available).
262
+ - **body**: Returns the message body content (combines plain text and HTML if both exist).
263
+ - **html**: Returns the HTML content of the message body (if available).
264
+ - **plain**: Returns the plain text content of the message body (if available).
265
+ - **alternative**: Returns the text content of the most appropriate alternative part (if message uses multipart/alternative).
266
+ - **recipient**: Extracts the recipient's email address from the "To" header (if available).
267
+ - **message_id**: Extracts the message ID from the message headers (if available).
268
+ - **subject**: Extracts the subject line from the message headers (if available).
269
+ - **sender**: Extracts the sender's email address from the "From" header (if available).
270
+ - **has_attachments**: Checks if the message or its nested parts contain attachments.
271
+ - **attachments**: Returns a list of Attachment objects for all attachments within the message.
272
+ - **is_unread, is_starred, is_important, etc.**: Boolean properties indicating the message's label status (unread, starred, important, spam, draft, trash).
273
+ - **created_date**: Converts the internalDate to a datetime object representing the message creation time.
274
+ - **date**: Parses the "Received" or "Date" header to get the message delivery date (if available).
275
+ - **date_string**: Formats the delivery date according to a specified format string (defaults to "%a, %d %b %Y %H:%M:%S %z").
276
+
277
+ ---
278
+
279
+ ###### Methods:
280
+ - **mark_as_read, mark_as_unread**: Toggles the unread flag for the message.
281
+ - **toogle_read_unread**: Shortcut to toggle the unread flag based on the current state.
282
+ - **mark_spam, mark_not_spam**: Toggles the spam label for the message.
283
+ - **toggle_spam**: Shortcut to toggle the spam label based on the current state.
284
+ - **move_to_trash, move_from_trash**: Toggles the trash label for the message.
285
+ - **toggle_trash**: Shortcut to toggle the trash label based on the current state.
286
+ - **mark_important, mark_not_important**: Toggles the important label for the message.
287
+ - **toggle_important**: Shortcut to toggle the important label based on the current state.
288
+ - **mark_starred, mark_not_starred**: Toggles the starred label for the message.
289
+ - **toggle_starred**: Shortcut to toggle the starred label based on the current state.
290
+ - **get_attachment(filename=None, id=None)**: Retrieves an attachment by filename or internal part ID.
291
+ - **get_labels**: Fetches and returns a list of GmailLabel objects for all labels associated with the message.
292
+ - **add_label(label), add_labels(labels)**: Adds labels to the message.
293
+ - **remove_label(label), remove_labels(labels)**: Removes labels from the message.
294
+ - **modify_labels(labels_to_add=None, labels_to_remove=None)**: Updates message labels by adding and/or removing labels.
295
+
296
+ Overall, this Message class provides a comprehensive way to access and manage individual email messages within a Gmail account. It allows you to retrieve message details, content, attachments, labels, and perform various actions like marking as read/unread, starring, labeling, and trash management.
297
+
298
+ ---
299
+
300
+ ### class Attachment:
301
+ ###### Properties:
302
+ - **filename (str)**: This attribute stores the filename of the attachment.
303
+ - **mimeType (str)**: This attribute stores the MIME type of the attachment content, which specifies the format of the data (e.g., image/jpeg, text/plain).
304
+ - **part_id (str)**: This attribute stores the internal identifier for the attachment within the message parts. This ID is used to download the attachment using the Gmail API.
305
+ - **google_service (GoogleService)**: This attribute holds a reference to the GoogleService object used for downloading the attachment. This object likely encapsulates authentication and communication details with the Gmail API.
306
+ - **id (Optional[str])**: This optional attribute stores the ID of the attachment generated by Gmail after a message is fetched using the .get() method. This ID might change for the same attachment across different fetches.
307
+ - **data (Optional[str])**: This optional attribute stores the Base64 encoded content of the attachment data downloaded from the API.
308
+
309
+ ###### Methods:
310
+ - **download() -> None**: This method downloads the attachment data from the Gmail API if it hasn't been downloaded already. It raises exceptions for errors like missing message ID or HTTP errors during download.
311
+ - **save(filepath: Optional[str] = None, overwrite: bool = False) -> None**: This method saves the attachment data to a file. It allows specifying the filepath and whether to overwrite existing files. It raises an error if the file already exists and overwrite is set to False.
312
+
313
+ Overall, the Attachment class provides functionality to manage attachments associated with Gmail messages. It handles downloading attachments from the API, storing downloaded data, and saving them to local files.
314
+
315
+ ---
316
+
317
+ ### class GetMessagesResponse:
318
+ ###### Attributes:
319
+ - **next_page_token (Optional[str], optional)**: Token for fetching the next page of results. Defaults to None.
320
+ - **existing_pages (Optional[str], optional)**: Used internally for pagination. Defaults to None. List like string of page tokens separated by GmailApi.SEPARATOR_SYMBOL
321
+ - **previous_page_token (Optional[List[str | None]], optional)**: Token for fetching the previous page of results (may be empty list). Defaults to [].
322
+ - **messages (List[Message])**: List of retrieved message objects.
@@ -0,0 +1,8 @@
1
+ LICENSE
2
+ README.md
3
+ setup.py
4
+ fast_gmail/fast_gmail.egg-info/PKG-INFO
5
+ fast_gmail/fast_gmail.egg-info/SOURCES.txt
6
+ fast_gmail/fast_gmail.egg-info/dependency_links.txt
7
+ fast_gmail/fast_gmail.egg-info/requires.txt
8
+ fast_gmail/fast_gmail.egg-info/top_level.txt
@@ -0,0 +1,4 @@
1
+ google-api-python-client==2.125.0
2
+ google-auth-httplib2==0.2.0
3
+ google-auth-oauthlib==1.2.0
4
+ typing-extensions==4.11.0
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,35 @@
1
+ from setuptools import setup
2
+ from setuptools import find_packages
3
+
4
+ with open("README.md", "r") as f:
5
+ long_descrition=f.read()
6
+
7
+ setup(
8
+ name="fast_gmail",
9
+ version="1.0.0",
10
+ description="GmailApi wrapper",
11
+ long_description=long_descrition, # Load description from README
12
+ long_description_content_type='text/markdown',
13
+ author="Aleti",
14
+ author_email="aleti.open.source@gmail.com",
15
+ url="https://github.com/aleti1/fast_gmail",
16
+ license="MIT",
17
+ requires=[],
18
+ package_dir={"":"fast_gmail"},
19
+ packages=find_packages(where="fast_gmail"),
20
+ python_requires=">=3.10",
21
+ install_requires=[
22
+ "google-api-python-client == 2.125.0",
23
+ "google-auth-httplib2 == 0.2.0",
24
+ "google-auth-oauthlib == 1.2.0",
25
+ "typing-extensions == 4.11.0"
26
+ ],
27
+ classifiers=[
28
+ 'Development Status :: 5 - Production/Stable',
29
+ 'Programming Language :: Python',
30
+ 'Programming Language :: Python :: 3.10',
31
+ 'Programming Language :: Python :: 3.11',
32
+ "License :: OSI Approved :: MIT License",
33
+ "Operating System :: OS Independent",
34
+ ],
35
+ )