import os.path import base64 import imaplib import email from email.header import decode_header import google.auth from google.oauth2.credentials import Credentials from google_auth_oauthlib.flow import InstalledAppFlow from google.auth.transport.requests import Request from bs4 import BeautifulSoup # Import BeautifulSoup for parsing HTML from datetime import datetime # If modifying these SCOPES, delete the file token.json. SCOPES = ['https://mail.google.com/'] def authenticate_gmail(): """Shows basic usage of the Gmail API. Lists the user's Gmail labels. """ creds = None # The file token.json stores the user's access and refresh tokens, and is # created automatically when the authorization flow completes for the first # time. token = r'/data/token.json' if os.path.isfile(token) is False: token = r'x:\substack\token.json' cred = r'/data/client_secret_396578640529-o4dsukvomuo43j5d4j0bogg17e3e8l7f.apps.googleusercontent.com.json' if os.path.isfile(cred) is False: cred = r'x:\substack\client_secret_396578640529-o4dsukvomuo43j5d4j0bogg17e3e8l7f.apps.googleusercontent.com.json' if os.path.exists(token): creds = Credentials.from_authorized_user_file(token, SCOPES) # If there are no (valid) credentials available, let the user log in. if not creds or not creds.valid: if creds and creds.expired and creds.refresh_token: creds.refresh(Request()) else: flow = InstalledAppFlow.from_client_secrets_file(cred, SCOPES) creds = flow.run_local_server(port=0) # Save the credentials for the next run with open(token, 'w') as token: token.write(creds.to_json()) return creds def generate_oauth2_string(username, access_token): return f"user={username}\1auth=Bearer {access_token}\1\1" def decode_mime_words(s): return ''.join( word.decode(encoding or 'utf-8') if isinstance(word, bytes) else word for word, encoding in decode_header(s) ) def get_verification_link(email_user, sender_email, start_time): creds = authenticate_gmail() auth_string = generate_oauth2_string(email_user, creds.token) mail = imaplib.IMAP4_SSL("imap.gmail.com") #mail.debug = 4 # Enable IMAP debug output for more detailed logs try: mail.authenticate('XOAUTH2', lambda x: auth_string) except imaplib.IMAP4.error as e: print(f"IMAP authentication error: {e}") return None mail.select("inbox") result, data = mail.search(None, f'(FROM "{sender_email}" SUBJECT "Finish signing in to Substack")') mail_ids = data[0] id_list = mail_ids.split() for num in reversed(id_list): # Check the most recent emails first result, data = mail.fetch(num, "(RFC822)") raw_email = data[0][1] msg = email.message_from_bytes(raw_email) # Decode and print the email subject subject = decode_mime_words(msg["Subject"]) # Get email date email_date_tuple = email.utils.parsedate_tz(msg["Date"]) email_timestamp = email.utils.mktime_tz(email_date_tuple) print(subject, start_time, email_timestamp) if abs(email_timestamp - start_time) > 12 * 3600: continue if msg.is_multipart(): for part in msg.walk(): if part.get_content_type() == "text/plain": body = part.get_payload(decode=True).decode() for line in body.split("\n"): if "http" in line: return line.strip() else: print("-----") body = msg.get_payload(decode=True).decode() soup = BeautifulSoup(body, 'html.parser') link = soup.find('a', href=True, text="Connectez-vous dès maintenant") if link: return link['href'] return None if __name__ == "__main__": email_user = "gael.honorez@gmail.com" sender_email = "no-reply@substack.com" verification_link = get_verification_link(email_user, sender_email) if verification_link: print("Verification link found:", verification_link) else: print("No verification link found.")