Squirtle
01-26-2024, 06:33 AM
This is a small class I wanted to share, designed for use on my personal mail server. I opted for IMAP over SMTP, so I haven't tested it with typical email providers like Gmail, Outlook, etc.
The purpose of this class is to automatically retrieve the activation code from a Neopets email, thereby streamlining the account activation process when used with an account creator. Once it secures the activation code, the corresponding email is deleted from the server, keeping the inbox clean.
import imaplib
import email
import time
from typing import Optional
class EmailClient:
def __init__(self) -> None:
self.email_address = "<EMAIL_ADDRESS>"
self.password = "<PASSWORD>"
self.mail_server = "<IMAP_SERVER>"
self.mail = imaplib.IMAP4_SSL(self.mail_server)
def login_to_account(self) -> None:
"""Logs into the email account."""
try:
self.mail.login(self.email_address, self.password)
except imaplib.IMAP4.error as e:
raise Exception(f"Failed to login: {e}")
def find_email(self, subject: str = "Welcome and Account", timeout: int = 300) -> Optional[str]:
"""
Searches and processes the email with the specified subject.
Args:
subject (str): The subject of the email to search for.
timeout (int): The time in seconds to keep searching before giving up.
Returns:
Optional[str]: The extracted activation code, or None if not found.
"""
self.mail.select("inbox", readonly=False)
end_time = time.time() + timeout
while time.time() < end_time:
typ, data = self.mail.search(None, f"(SUBJECT \"{subject}\")")
if data[0]:
activation_code = self.process_emails(data)
if activation_code:
return activation_code
time.sleep(5)
return None
def process_emails(self, data: str) -> Optional[str]:
"""Processes emails to find the one with the given subject and extracts the activation code."""
if not data[0].decode():
return None
for num in data[0].split():
typ, data = self.mail.fetch(num, "(RFC822)")
msg = email.message_from_bytes(data[0][1])
activation_code = self.extract_activation_code(msg)
if activation_code:
self.mail.store(num, '+FLAGS', '\\Deleted')
return activation_code
return None
def extract_activation_code(self, msg) -> Optional[str]:
"""Extracts the activation code from the email message."""
message = msg.get_payload(decode=True).decode('utf-8')
return self.get_substring_between_text(message, "box: ", ")")
def cleanup_mailbox(self) -> None:
"""Cleans up the mailbox by expunging deleted emails and logging out."""
self.mail.expunge()
self.mail.logout()
def get_activation_code(self) -> Optional[str]:
"""Main method to log in to the account and find the activation email."""
self.login_to_account()
activation_code = self.find_email()
self.cleanup_mailbox()
return activation_code
def get_substring_between_text(self, text: str, start: str, end: str) -> str:
"""
Extracts a substring from the provided text, located between specified start and end strings.
Args:
text (str): The text to search within.
start (str): The start delimiter of the substring.
end (str): The end delimiter of the substring.
Returns:
str: The extracted substring. Returns an empty string if delimiters are not found.
"""
try:
return text.split(start, 1)[1].split(end, 1)[0]
except IndexError:
return ""
The purpose of this class is to automatically retrieve the activation code from a Neopets email, thereby streamlining the account activation process when used with an account creator. Once it secures the activation code, the corresponding email is deleted from the server, keeping the inbox clean.
import imaplib
import email
import time
from typing import Optional
class EmailClient:
def __init__(self) -> None:
self.email_address = "<EMAIL_ADDRESS>"
self.password = "<PASSWORD>"
self.mail_server = "<IMAP_SERVER>"
self.mail = imaplib.IMAP4_SSL(self.mail_server)
def login_to_account(self) -> None:
"""Logs into the email account."""
try:
self.mail.login(self.email_address, self.password)
except imaplib.IMAP4.error as e:
raise Exception(f"Failed to login: {e}")
def find_email(self, subject: str = "Welcome and Account", timeout: int = 300) -> Optional[str]:
"""
Searches and processes the email with the specified subject.
Args:
subject (str): The subject of the email to search for.
timeout (int): The time in seconds to keep searching before giving up.
Returns:
Optional[str]: The extracted activation code, or None if not found.
"""
self.mail.select("inbox", readonly=False)
end_time = time.time() + timeout
while time.time() < end_time:
typ, data = self.mail.search(None, f"(SUBJECT \"{subject}\")")
if data[0]:
activation_code = self.process_emails(data)
if activation_code:
return activation_code
time.sleep(5)
return None
def process_emails(self, data: str) -> Optional[str]:
"""Processes emails to find the one with the given subject and extracts the activation code."""
if not data[0].decode():
return None
for num in data[0].split():
typ, data = self.mail.fetch(num, "(RFC822)")
msg = email.message_from_bytes(data[0][1])
activation_code = self.extract_activation_code(msg)
if activation_code:
self.mail.store(num, '+FLAGS', '\\Deleted')
return activation_code
return None
def extract_activation_code(self, msg) -> Optional[str]:
"""Extracts the activation code from the email message."""
message = msg.get_payload(decode=True).decode('utf-8')
return self.get_substring_between_text(message, "box: ", ")")
def cleanup_mailbox(self) -> None:
"""Cleans up the mailbox by expunging deleted emails and logging out."""
self.mail.expunge()
self.mail.logout()
def get_activation_code(self) -> Optional[str]:
"""Main method to log in to the account and find the activation email."""
self.login_to_account()
activation_code = self.find_email()
self.cleanup_mailbox()
return activation_code
def get_substring_between_text(self, text: str, start: str, end: str) -> str:
"""
Extracts a substring from the provided text, located between specified start and end strings.
Args:
text (str): The text to search within.
start (str): The start delimiter of the substring.
end (str): The end delimiter of the substring.
Returns:
str: The extracted substring. Returns an empty string if delimiters are not found.
"""
try:
return text.split(start, 1)[1].split(end, 1)[0]
except IndexError:
return ""