diff --git a/README.md b/README.md index d05b195..86e2835 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ Receive a message (e.g. activation code) from tempmail import EMail email = EMail() +print(email.address) # qwerty123@1secmail.com # ... request some email ... @@ -30,7 +31,7 @@ Get all messages in the inbox ```python from tempmail import EMail -email = EMail(username='example', domain='1secmail.com') +email = EMail('example@1secmail.com') inbox = email.get_inbox() for msg_info in inbox: @@ -41,7 +42,7 @@ Download an attachment ```python from tempmail import EMail -email = EMail('example@1secmail.com') +email = EMail(username='example', domain='1secmail.com') msg = email.wait_for_message() if msg.attachments: @@ -57,5 +58,31 @@ if msg.attachments: f.write(data) ``` +Get reddit activation code +```python +from tempmail import EMail + +def reddit_filter(msg): + return (msg.from_addr == 'noreply@reddit.com' and + msg.subject == 'Verify your Reddit email address') + +email = EMail(address='redditaccount@1secmail.com') +msg = email.wait_for_message(filter=reddit_filter) +# get_activation_code(html=msg.html_body) +``` + +Some other features: +```python +from tempmail.providers import OneSecMail + +email = OneSecMail() + +# request_email(email=email.address) + +# Accept only emails with a specific subject, raise error after 60 seconds +msg = email.wait_for_message(timeout=60, filter=lambda m: m.subject == 'Hello World!') +print(msg.body) +``` + ## License tempmail-python is licensed under the MIT License. See the [LICENSE](LICENSE) file for more information. diff --git a/setup.py b/setup.py index ee77b0f..3ca0457 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ def read(path: str) -> str: setup( name='tempmail-python', - version='2.2.1', + version='2.3.0', description='Python library for generating and managing temporary email addresses.', long_description=read('README.md'), long_description_content_type='text/markdown', diff --git a/tempmail/providers.py b/tempmail/providers.py index 654cd30..7ef3388 100644 --- a/tempmail/providers.py +++ b/tempmail/providers.py @@ -13,34 +13,43 @@ class OneSecMail: """1secmail.com API wrapper""" - def __init__(self, email: str | None = None, username: str | None = None, domain: str | None = None) -> None: - if email is not None: - username, domain = email.split('@') + def __init__(self, address: str | None = None, username: str | None = None, domain: str | None = None) -> None: + """Create a new 1secmail.com email address + + :param address: The full email address (username@domain) + :param username: The username of the email address (before the @) + :param domain: The domain of the email address (after the @) + """ + + if address is not None: + username, domain = address.split('@') if domain is not None and domain not in self.get_domains(): raise ValueError(f'Invalid domain: {domain}') - self.session = requests.Session() + self._session = requests.Session() self.username = username or utils.random_string(10) + """The username of the email address (before the @)""" self.domain = domain or random.choice(self.get_domains()) + """The domain of the email address (after the @)""" def get_inbox(self) -> list['OneSecMail.MessageInfo']: """Get the inbox of the email address""" - resp = self.session.get(f'https://www.1secmail.com/api/v1/?action=getMessages&login={self.username}&domain={self.domain}') + resp = self._session.get(f'https://www.1secmail.com/api/v1/?action=getMessages&login={self.username}&domain={self.domain}') resp.raise_for_status() return [OneSecMail.MessageInfo.from_dict(self, msg_info) for msg_info in resp.json()] @utils.cache def get_message(self, id: int) -> 'OneSecMail.Message': """Get a message from the inbox""" - resp = self.session.get(f'https://www.1secmail.com/api/v1/?action=readMessage&login={self.username}&domain={self.domain}&id={id}') + resp = self._session.get(f'https://www.1secmail.com/api/v1/?action=readMessage&login={self.username}&domain={self.domain}&id={id}') resp.raise_for_status() return OneSecMail.Message.from_dict(self, resp.json()) @utils.cache def download_attachment(self, id: int, file: str) -> bytes: """Download an attachment from a message as bytes""" - resp = self.session.get(f'https://www.1secmail.com/api/v1/?action=download&login={self.username}&domain={self.domain}&id={id}&file={file}') + resp = self._session.get(f'https://www.1secmail.com/api/v1/?action=download&login={self.username}&domain={self.domain}&id={id}&file={file}') resp.raise_for_status() return resp.content @@ -83,24 +92,33 @@ def __str__(self) -> str: @dataclass class MessageInfo: + """Information about a message in the inbox""" + id: int + "Message ID" from_addr: str + "Sender email address" subject: str + "Subject of the message" date_str: str - _mail: 'OneSecMail' + "Date the message was received in format YYYY-MM-DD HH:MM:SS" + _mail_instance: 'OneSecMail' @property def date(self) -> datetime: + """Date the message was received""" return datetime.fromisoformat(self.date_str) @property def message(self) -> 'OneSecMail.Message': - return self._mail.get_message(self.id) + """The full message""" + return self._mail_instance.get_message(self.id) @classmethod - def from_dict(cls, mail: 'OneSecMail', msg_info: dict[str, any]) -> 'OneSecMail.MessageInfo': + def from_dict(cls, mail_instance: 'OneSecMail', msg_info: dict[str, any]) -> 'OneSecMail.MessageInfo': + """Create a MessageInfo from a raw api response""" return cls( - _mail=mail, + _mail_instance=mail_instance, id=msg_info['id'], from_addr=msg_info['from'], subject=msg_info['subject'], @@ -109,28 +127,40 @@ def from_dict(cls, mail: 'OneSecMail', msg_info: dict[str, any]) -> 'OneSecMail. @dataclass class Message: + """Email message""" + id: int + "Message ID" from_addr: str + "Sender email address" subject: str + "Subject of the message" date_str: str + "Date the message was received in format YYYY-MM-DD HH:MM:SS" body: str + "Message body (html if exists, text otherwise)" text_body: str + "Message body (text format)" html_body: str - _mail: 'OneSecMail' + "Message body (html format)" + _mail_instance: 'OneSecMail' _attachments: list[dict[str, any]] @property def date(self) -> datetime: + """Date the message was received""" return datetime.fromisoformat(self.date_str) @property def attachments(self) -> list['OneSecMail.Attachment']: - return [OneSecMail.Attachment.from_dict(self._mail, self.id, attachment) for attachment in self._attachments] + """List of attachments in the message (files)""" + return [OneSecMail.Attachment.from_dict(self._mail_instance, self.id, attachment) for attachment in self._attachments] @classmethod - def from_dict(cls, mail: 'OneSecMail', msg: dict[str, any]) -> 'OneSecMail.Message': + def from_dict(cls, mail_instance: 'OneSecMail', msg: dict[str, any]) -> 'OneSecMail.Message': + """Create a Message from a raw api response""" return cls( - _mail=mail, + _mail_instance=mail_instance, _attachments=msg['attachments'], id=msg['id'], from_addr=msg['from'], @@ -143,20 +173,26 @@ def from_dict(cls, mail: 'OneSecMail', msg: dict[str, any]) -> 'OneSecMail.Messa @dataclass class Attachment: + """Email attachment""" + filename: str + "Name of the file of the attachment" content_type: str + "MIME type of the attachment" size: int - _mail: 'OneSecMail' + "Size of the attachment in bytes" + _mail_instance: 'OneSecMail' _message_id: int def download(self) -> bytes: """Download the attachment as bytes""" - return self._mail.download_attachment(self._message_id, self.filename) + return self._mail_instance.download_attachment(self._message_id, self.filename) @classmethod - def from_dict(cls, mail: 'OneSecMail', message_id: int, attachment: dict[str, any]) -> 'OneSecMail.Attachment': + def from_dict(cls, mail_instance: 'OneSecMail', message_id: int, attachment: dict[str, any]) -> 'OneSecMail.Attachment': + """Create an Attachment from a raw api response""" return cls( - _mail=mail, + _mail_instance=mail_instance, _message_id=message_id, filename=attachment['filename'], content_type=attachment['contentType'],