-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgmail_draft_creator.py
More file actions
163 lines (132 loc) · 5.42 KB
/
Copy pathgmail_draft_creator.py
File metadata and controls
163 lines (132 loc) · 5.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#!/usr/bin/env python3
"""
Reads all email drafts from the email_drafts/ folder and creates
them as real Gmail drafts in your Gmail account.
Requires client_secret.json (already set up from Google Sheets step).
Deletes token.json first if scopes have changed.
"""
import os
import base64
import glob
import yaml
from email.mime.text import MIMEText
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from googleapiclient.discovery import build
SCOPES = [
'https://www.googleapis.com/auth/spreadsheets',
'https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/gmail.compose',
]
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
TOKEN_FILE = os.path.join(SCRIPT_DIR, 'token.json')
CREDENTIALS_FILE = os.path.join(SCRIPT_DIR, 'client_secret.json')
with open(os.path.join(SCRIPT_DIR, 'config.yaml'), 'r') as _f:
_cfg = yaml.safe_load(_f)
DRAFTS_DIR = os.path.join(SCRIPT_DIR, _cfg['files']['email_drafts_dir'])
CHILD_NAME = _cfg['child']['name']
CHILD_DOB = _cfg['child']['dob_display']
def authenticate():
creds = None
if os.path.exists(TOKEN_FILE):
creds = Credentials.from_authorized_user_file(TOKEN_FILE, SCOPES)
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
try:
creds.refresh(Request())
except Exception:
creds = None
if not creds:
flow = InstalledAppFlow.from_client_secrets_file(CREDENTIALS_FILE, SCOPES)
creds = flow.run_local_server(port=0)
with open(TOKEN_FILE, 'w') as f:
f.write(creds.to_json())
print("✅ Authentication successful.")
return creds
def parse_draft_file(filepath):
"""Parse a .txt draft file into (to, subject, body)."""
with open(filepath, 'r', encoding='utf-8') as f:
lines = f.read().splitlines()
to_addr = ''
subject = ''
body_lines = []
in_body = False
for line in lines:
if line.startswith('TO:'):
raw_to = line[3:].strip()
# Extract email address from lines like "TO: name@example.com" or
# "TO: Use contact form at ..." (no real email — skip or use placeholder)
if '@' in raw_to and 'contact form' not in raw_to.lower():
# grab just the email token
for token in raw_to.split():
if '@' in token:
to_addr = token.strip('<>(),')
break
else:
to_addr = '' # will be left blank for form-only contacts
elif line.startswith('SUBJECT:'):
subject = line[8:].strip()
elif line.startswith('PHONE:'):
continue # skip phone lines
elif not in_body and line == '' and subject:
in_body = True # blank line after headers = start of body
elif in_body:
body_lines.append(line)
body = '\n'.join(body_lines).strip()
return to_addr, subject, body
def create_gmail_draft(service, to_addr, subject, body):
"""Create a single Gmail draft."""
message = MIMEText(body, 'plain')
message['to'] = to_addr if to_addr else ''
message['subject'] = subject
raw = base64.urlsafe_b64encode(message.as_bytes()).decode('utf-8')
draft_body = {'message': {'raw': raw}}
draft = service.users().drafts().create(userId='me', body=draft_body).execute()
return draft['id']
def main():
print("=" * 60)
print(f" Gmail Draft Creator — {_cfg['search']['season']} Camps {_cfg['search']['year']}")
print(f" Child: {CHILD_NAME} | DOB: {CHILD_DOB}")
print("=" * 60)
if not os.path.exists(CREDENTIALS_FILE):
print(f"\n❌ '{CREDENTIALS_FILE}' not found.")
raise SystemExit(1)
# Must delete old token so Gmail scope gets added
if os.path.exists(TOKEN_FILE):
os.remove(TOKEN_FILE)
print("🔄 Cleared old token to add Gmail permission...")
print("\n🔐 Authenticating (browser will open)...")
creds = authenticate()
service = build('gmail', 'v1', credentials=creds)
draft_files = sorted(glob.glob(os.path.join(DRAFTS_DIR, '*.txt')))
if not draft_files:
print(f"❌ No .txt files found in '{DRAFTS_DIR}/'")
raise SystemExit(1)
print(f"\n📧 Creating {len(draft_files)} Gmail drafts...\n")
success, skipped = 0, 0
for filepath in draft_files:
filename = os.path.basename(filepath)
to_addr, subject, body = parse_draft_file(filepath)
if not subject or not body:
print(f" ⚠️ Skipped (could not parse): {filename}")
skipped += 1
continue
try:
create_gmail_draft(service, to_addr, subject, body)
to_display = to_addr if to_addr else '(no email — use contact form)'
print(f" ✅ {filename}")
print(f" To: {to_display}")
print(f" Subject: {subject[:70]}")
print()
success += 1
except Exception as e:
print(f" ❌ Failed: {filename} — {e}")
skipped += 1
print("-" * 60)
print(f"✅ {success} drafts created in Gmail")
if skipped:
print(f"⚠️ {skipped} skipped (no email address or parse error)")
print("\nOpen Gmail Drafts: https://mail.google.com/mail/#drafts")
if __name__ == '__main__':
main()