gmail-api-client 0.1.0__py3-none-any.whl

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,15 @@
1
+ from .client import GmailClient
2
+ from .exceptions import (
3
+ GmailAPIError,
4
+ GmailAuthenticationError,
5
+ GmailDraftError,
6
+ GmailSendError,
7
+ )
8
+
9
+ __all__ = [
10
+ "GmailClient",
11
+ "GmailAPIError",
12
+ "GmailAuthenticationError",
13
+ "GmailDraftError",
14
+ "GmailSendError",
15
+ ]
@@ -0,0 +1,176 @@
1
+ import base64
2
+ import os
3
+ from email.message import EmailMessage
4
+ from typing import Optional
5
+
6
+ from google.auth.transport.requests import Request
7
+ from google.oauth2.credentials import Credentials
8
+ from google_auth_oauthlib.flow import InstalledAppFlow
9
+ from googleapiclient.discovery import Resource, build
10
+ from googleapiclient.errors import HttpError
11
+
12
+ from .exceptions import (
13
+ GmailAuthenticationError,
14
+ GmailDraftError,
15
+ GmailSendError,
16
+ )
17
+
18
+
19
+ SCOPES = [
20
+ "https://www.googleapis.com/auth/gmail.compose"
21
+ ]
22
+
23
+
24
+ class GmailClient:
25
+ """Client for interacting with the Gmail API."""
26
+
27
+ def __init__(
28
+ self,
29
+ credentials_path: str = "credentials.json",
30
+ token_path: str = "token.json",
31
+ ):
32
+ self.credentials_path = credentials_path
33
+ self.token_path = token_path
34
+ self.service = self._authenticate()
35
+
36
+ def _authenticate(self) -> Resource:
37
+ """Authenticate the user and create Gmail API service."""
38
+
39
+ creds: Optional[Credentials] = None
40
+
41
+ try:
42
+ if os.path.exists(self.token_path):
43
+ creds = Credentials.from_authorized_user_file(
44
+ self.token_path,
45
+ SCOPES,
46
+ )
47
+
48
+ if not creds or not creds.valid:
49
+
50
+ if creds and creds.expired and creds.refresh_token:
51
+ creds.refresh(Request())
52
+
53
+ else:
54
+ flow = InstalledAppFlow.from_client_secrets_file(
55
+ self.credentials_path,
56
+ SCOPES,
57
+ )
58
+
59
+ creds = flow.run_local_server(port=0)
60
+
61
+ with open(self.token_path, "w") as token:
62
+ token.write(creds.to_json())
63
+
64
+ return build(
65
+ "gmail",
66
+ "v1",
67
+ credentials=creds,
68
+ )
69
+
70
+ except Exception as error:
71
+ raise GmailAuthenticationError(
72
+ f"Failed to authenticate with Gmail: {error}"
73
+ ) from error
74
+
75
+ @staticmethod
76
+ def _create_message(
77
+ to: str,
78
+ subject: str,
79
+ body: str,
80
+ sender: Optional[str] = None,
81
+ ) -> EmailMessage:
82
+ """Create an email message."""
83
+
84
+ message = EmailMessage()
85
+
86
+ message.set_content(body)
87
+
88
+ if sender:
89
+ message["From"] = sender
90
+
91
+ message["To"] = to
92
+ message["Subject"] = subject
93
+
94
+ return message
95
+
96
+ @staticmethod
97
+ def _encode_message(message: EmailMessage) -> str:
98
+ """Encode an email for the Gmail API."""
99
+
100
+ return base64.urlsafe_b64encode(
101
+ message.as_bytes()
102
+ ).decode()
103
+
104
+ def create_draft(
105
+ self,
106
+ to: str,
107
+ subject: str,
108
+ body: str,
109
+ sender: Optional[str] = None,
110
+ ):
111
+ """Create a Gmail draft."""
112
+
113
+ try:
114
+ message = self._create_message(
115
+ to=to,
116
+ subject=subject,
117
+ body=body,
118
+ sender=sender,
119
+ )
120
+
121
+ encoded_message = self._encode_message(message)
122
+
123
+ return (
124
+ self.service.users()
125
+ .drafts()
126
+ .create(
127
+ userId="me",
128
+ body={
129
+ "message": {
130
+ "raw": encoded_message,
131
+ }
132
+ },
133
+ )
134
+ .execute()
135
+ )
136
+
137
+ except HttpError as error:
138
+ raise GmailDraftError(
139
+ f"Failed to create Gmail draft: {error}"
140
+ ) from error
141
+
142
+ def send_mail(
143
+ self,
144
+ to: str,
145
+ subject: str,
146
+ body: str,
147
+ sender: Optional[str] = None,
148
+ ):
149
+ """Send an email through Gmail."""
150
+
151
+ try:
152
+ message = self._create_message(
153
+ to=to,
154
+ subject=subject,
155
+ body=body,
156
+ sender=sender,
157
+ )
158
+
159
+ encoded_message = self._encode_message(message)
160
+
161
+ return (
162
+ self.service.users()
163
+ .messages()
164
+ .send(
165
+ userId="me",
166
+ body={
167
+ "raw": encoded_message,
168
+ },
169
+ )
170
+ .execute()
171
+ )
172
+
173
+ except HttpError as error:
174
+ raise GmailSendError(
175
+ f"Failed to send Gmail message: {error}"
176
+ ) from error
@@ -0,0 +1,14 @@
1
+ class GmailAPIError(Exception):
2
+ """Base exception for all Gmail API client errors."""
3
+
4
+
5
+ class GmailAuthenticationError(GmailAPIError):
6
+ """Raised when Gmail authentication fails."""
7
+
8
+
9
+ class GmailDraftError(GmailAPIError):
10
+ """Raised when creating a Gmail draft fails."""
11
+
12
+
13
+ class GmailSendError(GmailAPIError):
14
+ """Raised when sending an email fails."""
@@ -0,0 +1,523 @@
1
+ Metadata-Version: 2.4
2
+ Name: gmail-api-client
3
+ Version: 0.1.0
4
+ Summary: A simple Python client for Gmail API automation
5
+ Author: Asheesh Singh
6
+ License: MIT
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: google-api-python-client>=2.0.0
10
+ Requires-Dist: google-auth-httplib2>=0.2.0
11
+ Requires-Dist: google-auth-oauthlib>=1.0.0
12
+ Provides-Extra: dev
13
+ Requires-Dist: pytest>=8.0; extra == "dev"
14
+
15
+ # Gmail API Automation
16
+
17
+ A simple Python project for interacting with Gmail using the **Gmail API** and **OAuth 2.0**.
18
+
19
+ This project supports:
20
+
21
+ * Creating Gmail drafts
22
+ * Sending emails
23
+ * OAuth 2.0 authentication
24
+ * Reusing authenticated credentials through `token.json`
25
+ * Programmatic email composition using Python's `EmailMessage`
26
+
27
+ ---
28
+
29
+ ## Features
30
+
31
+ ### Create Drafts
32
+
33
+ Create a Gmail draft programmatically:
34
+
35
+ ```python
36
+ gmail_create_draft(
37
+ to="recipient@example.com",
38
+ subject="Automated draft",
39
+ body="This is an automated draft."
40
+ )
41
+ ```
42
+
43
+ ### Send Emails
44
+
45
+ Send an email directly through Gmail:
46
+
47
+ ```python
48
+ gmail_send_mail(
49
+ to="recipient@example.com",
50
+ subject="Automated email",
51
+ body="This email was sent using the Gmail API."
52
+ )
53
+ ```
54
+
55
+ ---
56
+
57
+ ## Architecture
58
+
59
+ ```text
60
+ Python Application
61
+ │
62
+ ▼
63
+ OAuth 2.0 Authentication
64
+ │
65
+ ▼
66
+ Gmail API
67
+ │
68
+ ├── Create Draft
69
+ │
70
+ └── Send Email
71
+ ```
72
+
73
+ The application uses Google's OAuth 2.0 flow for a local desktop application.
74
+
75
+ ```text
76
+ credentials.json
77
+ │
78
+ ▼
79
+ Google OAuth Login
80
+ │
81
+ ▼
82
+ token.json
83
+ │
84
+ ▼
85
+ Gmail API Client
86
+ ```
87
+
88
+ ---
89
+
90
+ ## Tech Stack
91
+
92
+ * Python 3
93
+ * Gmail API
94
+ * Google OAuth 2.0
95
+ * `google-api-python-client`
96
+ * `google-auth`
97
+ * `google-auth-oauthlib`
98
+
99
+ ---
100
+
101
+ ## Project Structure
102
+
103
+ ```text
104
+ gmail/
105
+ │
106
+ ├── main.py
107
+ ├── credentials.json
108
+ ├── token.json
109
+ ├── .gitignore
110
+ └── README.md
111
+ ```
112
+
113
+ ### Files
114
+
115
+ | File | Description |
116
+ | ------------------ | ----------------------------------------------------- |
117
+ | `main.py` | Gmail API implementation |
118
+ | `credentials.json` | OAuth client credentials downloaded from Google Cloud |
119
+ | `token.json` | Generated OAuth access/refresh token |
120
+ | `.gitignore` | Prevents credentials from being committed |
121
+ | `README.md` | Project documentation |
122
+
123
+ ---
124
+
125
+ # Setup
126
+
127
+ ## 1. Create a Google Cloud Project
128
+
129
+ Go to the [Google Cloud Console](https://console.cloud.google.com/).
130
+
131
+ Create a new project or use an existing project.
132
+
133
+ ---
134
+
135
+ ## 2. Enable Gmail API
136
+
137
+ Open the API Library and enable:
138
+
139
+ **Gmail API**
140
+
141
+ You can also enable it using the Google Cloud CLI:
142
+
143
+ ```bash
144
+ gcloud services enable gmail.googleapis.com
145
+ ```
146
+
147
+ ---
148
+
149
+ ## 3. Configure OAuth Consent Screen
150
+
151
+ In Google Cloud Console:
152
+
153
+ ```text
154
+ Google Auth Platform
155
+ ↓
156
+ Branding
157
+ ↓
158
+ Configure OAuth consent screen
159
+ ```
160
+
161
+ Configure the application information.
162
+
163
+ For a personal/local application, you can use the appropriate testing configuration.
164
+
165
+ Add your Gmail account as a test user if the application is in testing mode.
166
+
167
+ ---
168
+
169
+ ## 4. Create OAuth Client
170
+
171
+ Go to:
172
+
173
+ ```text
174
+ Google Auth Platform
175
+ ↓
176
+ Clients
177
+ ↓
178
+ Create Client
179
+ ```
180
+
181
+ Select:
182
+
183
+ ```text
184
+ Application type: Desktop app
185
+ ```
186
+
187
+ Download the generated credentials file.
188
+
189
+ Rename it:
190
+
191
+ ```text
192
+ credentials.json
193
+ ```
194
+
195
+ Place it in the project root:
196
+
197
+ ```text
198
+ gmail/
199
+ ├── main.py
200
+ ├── credentials.json
201
+ └── README.md
202
+ ```
203
+
204
+ ---
205
+
206
+ # Installation
207
+
208
+ Create a virtual environment:
209
+
210
+ ```bash
211
+ python -m venv .venv
212
+ ```
213
+
214
+ Activate it.
215
+
216
+ ### macOS / Linux
217
+
218
+ ```bash
219
+ source .venv/bin/activate
220
+ ```
221
+
222
+ ### Windows
223
+
224
+ ```bash
225
+ .venv\Scripts\activate
226
+ ```
227
+
228
+ Install dependencies:
229
+
230
+ ```bash
231
+ pip install -U \
232
+ google-api-python-client \
233
+ google-auth-httplib2 \
234
+ google-auth-oauthlib
235
+ ```
236
+
237
+ ---
238
+
239
+ # Gmail OAuth Scope
240
+
241
+ The application uses:
242
+
243
+ ```python
244
+ SCOPES = [
245
+ "https://www.googleapis.com/auth/gmail.compose"
246
+ ]
247
+ ```
248
+
249
+ The `gmail.compose` scope allows the application to:
250
+
251
+ * Create drafts
252
+ * Read/write draft content
253
+ * Send messages
254
+
255
+ The application does not request full Gmail access when it isn't required.
256
+
257
+ ---
258
+
259
+ # Authentication
260
+
261
+ The first time you run:
262
+
263
+ ```bash
264
+ python main.py
265
+ ```
266
+
267
+ Google will open a browser window.
268
+
269
+ Sign in with the Gmail account you want the application to use and grant the requested Gmail permissions.
270
+
271
+ After successful authentication, the application creates:
272
+
273
+ ```text
274
+ token.json
275
+ ```
276
+
277
+ The token is reused on subsequent executions.
278
+
279
+ You normally won't need to authenticate again unless the token is revoked or the requested scopes change.
280
+
281
+ ---
282
+
283
+ # Important: Changing OAuth Scopes
284
+
285
+ If you change:
286
+
287
+ ```python
288
+ SCOPES = [...]
289
+ ```
290
+
291
+ delete the existing token:
292
+
293
+ ```bash
294
+ rm token.json
295
+ ```
296
+
297
+ Then run:
298
+
299
+ ```bash
300
+ python main.py
301
+ ```
302
+
303
+ Google will request authorization again with the new scopes.
304
+
305
+ ---
306
+
307
+ # Creating a Draft
308
+
309
+ Example:
310
+
311
+ ```python
312
+ gmail_create_draft(
313
+ to="recipient@example.com",
314
+ subject="Automated Draft",
315
+ body="This is an automated draft."
316
+ )
317
+ ```
318
+
319
+ The Gmail API returns a draft object containing the draft ID and associated message information.
320
+
321
+ Example output:
322
+
323
+ ```text
324
+ Draft created successfully.
325
+ Draft ID: r123456789
326
+ ```
327
+
328
+ The draft will appear in the Gmail **Drafts** folder.
329
+
330
+ ---
331
+
332
+ # Sending an Email
333
+
334
+ Example:
335
+
336
+ ```python
337
+ gmail_send_mail(
338
+ to="recipient@example.com",
339
+ subject="Automated Email",
340
+ body="This email was sent using the Gmail API."
341
+ )
342
+ ```
343
+
344
+ Example output:
345
+
346
+ ```text
347
+ Email sent successfully.
348
+ Message ID: 18abc123456
349
+ ```
350
+
351
+ The message will be sent directly through the authenticated Gmail account.
352
+
353
+ ---
354
+
355
+ # Example
356
+
357
+ ```python
358
+ from main import gmail_create_draft, gmail_send_mail
359
+
360
+
361
+ # Create a draft
362
+ gmail_create_draft(
363
+ to="recipient@example.com",
364
+ subject="Test Draft",
365
+ body="This is a test draft."
366
+ )
367
+
368
+
369
+ # Send an email
370
+ gmail_send_mail(
371
+ to="recipient@example.com",
372
+ subject="Test Email",
373
+ body="This is a test email."
374
+ )
375
+ ```
376
+
377
+ ---
378
+
379
+ # Security
380
+
381
+ **Never commit your OAuth credentials to Git.**
382
+
383
+ Add the following to `.gitignore`:
384
+
385
+ ```gitignore
386
+ credentials.json
387
+ token.json
388
+ .venv/
389
+ __pycache__/
390
+ *.pyc
391
+ .env
392
+ ```
393
+
394
+ Your `.gitignore` should look like:
395
+
396
+ ```gitignore
397
+ # Google OAuth credentials
398
+ credentials.json
399
+ token.json
400
+
401
+ # Python
402
+ .venv/
403
+ __pycache__/
404
+ *.pyc
405
+
406
+ # Environment variables
407
+ .env
408
+
409
+ # macOS
410
+ .DS_Store
411
+ ```
412
+
413
+ If credentials are accidentally committed to a public repository, revoke them immediately from Google Cloud and generate new credentials.
414
+
415
+ ---
416
+
417
+ # Troubleshooting
418
+
419
+ ## 403: Insufficient Authentication Scopes
420
+
421
+ If you see:
422
+
423
+ ```text
424
+ HttpError 403
425
+ Request had insufficient authentication scopes
426
+ ```
427
+
428
+ delete the existing token:
429
+
430
+ ```bash
431
+ rm token.json
432
+ ```
433
+
434
+ Then authenticate again:
435
+
436
+ ```bash
437
+ python main.py
438
+ ```
439
+
440
+ Make sure the required scope is present:
441
+
442
+ ```python
443
+ SCOPES = [
444
+ "https://www.googleapis.com/auth/gmail.compose"
445
+ ]
446
+ ```
447
+
448
+ ---
449
+
450
+ ## 403: Gmail API Has Not Been Used
451
+
452
+ If you see:
453
+
454
+ ```text
455
+ Gmail API has not been used in project ... before
456
+ or it is disabled
457
+ ```
458
+
459
+ enable Gmail API for the Google Cloud project associated with your OAuth credentials:
460
+
461
+ ```bash
462
+ gcloud services enable gmail.googleapis.com
463
+ ```
464
+
465
+ You can also enable it from the Google Cloud Console.
466
+
467
+ After enabling the API, wait a few minutes and run:
468
+
469
+ ```bash
470
+ python main.py
471
+ ```
472
+
473
+ again.
474
+
475
+ ---
476
+
477
+ ## Invalid Credentials
478
+
479
+ If you see an OAuth/client configuration error, verify that:
480
+
481
+ ```text
482
+ credentials.json
483
+ ```
484
+
485
+ is present in the project root and was downloaded from the correct Google Cloud project.
486
+
487
+ ---
488
+
489
+ # Future Improvements
490
+
491
+ Possible extensions for this project:
492
+
493
+ * HTML email support
494
+ * CC and BCC
495
+ * File attachments
496
+ * Inline images
497
+ * Reply to an existing email
498
+ * Reply within an existing Gmail thread
499
+ * Forward emails
500
+ * Search Gmail messages
501
+ * Read emails
502
+ * Manage labels
503
+ * Delete drafts
504
+ * Schedule email workflows
505
+ * Email templates
506
+ * CLI interface
507
+ * FastAPI service around Gmail operations
508
+ * AI-powered email generation
509
+
510
+ ---
511
+
512
+ # References
513
+
514
+ * [Gmail API](https://developers.google.com/workspace/gmail/api)
515
+ * [Gmail API Python Quickstart](https://developers.google.com/workspace/gmail/api/quickstart/python)
516
+ * [Google OAuth 2.0](https://developers.google.com/identity/protocols/oauth2)
517
+ * [Google Cloud Console](https://console.cloud.google.com/)
518
+
519
+ ---
520
+
521
+ ## License
522
+
523
+ This project is for educational and personal automation purposes.
@@ -0,0 +1,7 @@
1
+ gmail_api_client/__init__.py,sha256=judvXF5iFEiv3_rOQE_3REDFlKfYG1gi75om7He5wnk,281
2
+ gmail_api_client/client.py,sha256=hq0hfYdumTKyOpYeOUSJyF6KN56yUs3CPWaoPHDxtJg,4475
3
+ gmail_api_client/exceptions.py,sha256=TogvdqGHjpUgQY46TJ3uSNb5lv5w4fOraga8A1omM3M,365
4
+ gmail_api_client-0.1.0.dist-info/METADATA,sha256=LDYlNyOalhAV5gDAy3ICB72wNEp5o3IOSXB581DV7xY,8380
5
+ gmail_api_client-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
6
+ gmail_api_client-0.1.0.dist-info/top_level.txt,sha256=hKd0VtNHuyjchIQnXC2s1-y7oC-vCmtjvsqIxPSydWA,17
7
+ gmail_api_client-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ gmail_api_client