Compare commits
2 Commits
100d66479a
...
d22ce578c8
Author | SHA1 | Date | |
---|---|---|---|
d22ce578c8 | |||
8347e831af |
94
README.md
94
README.md
@ -1,3 +1,95 @@
|
|||||||
# pymailer
|
# pymailer
|
||||||
|
|
||||||
Very simple CLI email client written in Python
|
A very simple CLI mail client written in Python. Written to fulfil some specific requirements I had for automated mail server testing.
|
||||||
|
|
||||||
|
Relies on Python's built in `smptlib` for sending mails, and [IMAPClient](https://pypi.org/project/IMAPClient/) for inbox actions.
|
||||||
|
|
||||||
|
Pymailer can
|
||||||
|
- Send an email via SMTP
|
||||||
|
- Connect to an inbox via IMAP and
|
||||||
|
- List emails
|
||||||
|
- Search for emails
|
||||||
|
- Delete emails that match search criteria
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
usage: pymailer [-h] {send,message_list,message_search,message_delete} ...
|
||||||
|
|
||||||
|
Simple mail client for sending emails, and checking and manipulating an inbox
|
||||||
|
|
||||||
|
optional arguments:
|
||||||
|
-h, --help show this help message and exit
|
||||||
|
|
||||||
|
subcommands:
|
||||||
|
{send,message_list,message_search,message_delete}
|
||||||
|
available subcommands
|
||||||
|
send send an email
|
||||||
|
message_list lists all messages in an inbox via IMAP
|
||||||
|
message_search search an inbox for messages via IMAP
|
||||||
|
message_delete deletes messages in an inbox via IMAP
|
||||||
|
```
|
||||||
|
```
|
||||||
|
usage: pymailer send [-h] --smtp_host SMTP_HOST --smtp_user SMTP_USER --smtp_password SMTP_PASSWORD --from_addr FROM_ADDR --to_addr TO_ADDR --subject SUBJECT --body BODY [--debug_level {0,1,2}]
|
||||||
|
|
||||||
|
optional arguments:
|
||||||
|
-h, --help show this help message and exit
|
||||||
|
--smtp_host SMTP_HOST
|
||||||
|
SMTP server hostname
|
||||||
|
--smtp_user SMTP_USER
|
||||||
|
SMTP server logon username
|
||||||
|
--smtp_password SMTP_PASSWORD
|
||||||
|
SMTP server logon password
|
||||||
|
--from_addr FROM_ADDR
|
||||||
|
Address to send email from
|
||||||
|
--to_addr TO_ADDR Address to send email to
|
||||||
|
--subject SUBJECT Email subject
|
||||||
|
--body BODY Email body
|
||||||
|
--debug_level {0,1,2}
|
||||||
|
Debug level, higher number is more verbose
|
||||||
|
```
|
||||||
|
```
|
||||||
|
usage: pymailer message_list [-h] --imap_host IMAP_HOST --imap_user IMAP_USER --imap_password IMAP_PASSWORD [--folder FOLDER]
|
||||||
|
|
||||||
|
optional arguments:
|
||||||
|
-h, --help show this help message and exit
|
||||||
|
--imap_host IMAP_HOST
|
||||||
|
IMAP server hostname
|
||||||
|
--imap_user IMAP_USER
|
||||||
|
IMAP server logon username
|
||||||
|
--imap_password IMAP_PASSWORD
|
||||||
|
IMAP server logon password
|
||||||
|
--folder FOLDER Inbox folder
|
||||||
|
```
|
||||||
|
```
|
||||||
|
usage: pymailer message_search [-h] --imap_host IMAP_HOST --imap_user IMAP_USER --imap_password IMAP_PASSWORD [--folder FOLDER] --search_string [SEARCH_STRING ...]
|
||||||
|
|
||||||
|
optional arguments:
|
||||||
|
-h, --help show this help message and exit
|
||||||
|
--imap_host IMAP_HOST
|
||||||
|
IMAP server hostname
|
||||||
|
--imap_user IMAP_USER
|
||||||
|
IMAP server logon username
|
||||||
|
--imap_password IMAP_PASSWORD
|
||||||
|
IMAP server logon password
|
||||||
|
--folder FOLDER Inbox folder
|
||||||
|
--search_string [SEARCH_STRING ...]
|
||||||
|
Search string e.g 'FROM' 'user@example.com'. Space separated
|
||||||
|
```
|
||||||
|
```
|
||||||
|
usage: pymailer message_delete [-h] --imap_host IMAP_HOST --imap_user IMAP_USER --imap_password IMAP_PASSWORD [--folder FOLDER] --search_string [SEARCH_STRING ...] [--dry_run {true,false}]
|
||||||
|
|
||||||
|
optional arguments:
|
||||||
|
-h, --help show this help message and exit
|
||||||
|
--imap_host IMAP_HOST
|
||||||
|
IMAP server hostname
|
||||||
|
--imap_user IMAP_USER
|
||||||
|
IMAP server logon username
|
||||||
|
--imap_password IMAP_PASSWORD
|
||||||
|
IMAP server logon password
|
||||||
|
--folder FOLDER Inbox folder
|
||||||
|
--search_string [SEARCH_STRING ...]
|
||||||
|
Search string for emails that will be deleted e.g 'FROM' 'user@example.com'. Space separated
|
||||||
|
--dry_run {true,false}
|
||||||
|
List emails that will be deleted, but don't delete them
|
||||||
|
```
|
110
pymailer.py
Normal file
110
pymailer.py
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
import argparse
|
||||||
|
import smtplib
|
||||||
|
from imapclient import IMAPClient
|
||||||
|
from email.message import EmailMessage
|
||||||
|
from email import message_from_bytes
|
||||||
|
from email.utils import localtime
|
||||||
|
|
||||||
|
def send_mail(args):
|
||||||
|
|
||||||
|
msg = EmailMessage()
|
||||||
|
|
||||||
|
# Create email body
|
||||||
|
msg.set_content(args.body)
|
||||||
|
|
||||||
|
# Set email headers
|
||||||
|
msg['Subject'] = args.subject
|
||||||
|
msg['From'] = args.from_addr
|
||||||
|
msg['To'] = args.to_addr
|
||||||
|
msg['Date'] = localtime()
|
||||||
|
|
||||||
|
smtpObj = smtplib.SMTP_SSL(host=args.smtp_host)
|
||||||
|
smtpObj.set_debuglevel(args.debug_level)
|
||||||
|
smtpObj.login(args.smtp_user, args.smtp_password)
|
||||||
|
smtpObj.send_message(msg)
|
||||||
|
|
||||||
|
def message_list(args):
|
||||||
|
|
||||||
|
imap = IMAPClient(args.imap_host)
|
||||||
|
imap.login(args.imap_user, args.imap_password)
|
||||||
|
imap.select_folder(args.folder, readonly=True)
|
||||||
|
messages = imap.search(['FROM', ''])
|
||||||
|
for uid, message_data in imap.fetch(messages, "RFC822").items():
|
||||||
|
email_message = message_from_bytes(message_data[b"RFC822"])
|
||||||
|
print(uid, email_message)
|
||||||
|
imap.logout()
|
||||||
|
|
||||||
|
def message_search(args):
|
||||||
|
|
||||||
|
imap = IMAPClient(args.imap_host)
|
||||||
|
imap.login(args.imap_user, args.imap_password)
|
||||||
|
imap.select_folder(args.folder, readonly=True)
|
||||||
|
messages = imap.search(args.search_string)
|
||||||
|
for uid, message_data in imap.fetch(messages, "RFC822").items():
|
||||||
|
email_message = message_from_bytes(message_data[b"RFC822"])
|
||||||
|
print(uid, email_message)
|
||||||
|
imap.logout()
|
||||||
|
|
||||||
|
def message_delete(args):
|
||||||
|
|
||||||
|
imap = IMAPClient(args.imap_host)
|
||||||
|
imap.login(args.imap_user, args.imap_password)
|
||||||
|
|
||||||
|
if args.dry_run == 'true':
|
||||||
|
print('--dry_run is true, opening IMAP folder as readonly. No actions will be persistent')
|
||||||
|
imap.select_folder(args.folder, readonly=True)
|
||||||
|
else:
|
||||||
|
imap.select_folder(args.folder)
|
||||||
|
messages = imap.search(args.search_string)
|
||||||
|
for uid, message_data in imap.fetch(messages, "RFC822").items():
|
||||||
|
email_message = message_from_bytes(message_data[b"RFC822"])
|
||||||
|
date = email_message.get("Date")
|
||||||
|
from_addr = email_message.get("From")
|
||||||
|
to_addr = email_message.get("To")
|
||||||
|
subject = email_message.get("Subject")
|
||||||
|
imap.delete_messages(uid)
|
||||||
|
imap.expunge(uid)
|
||||||
|
print('\nMessage with uid %s deleted:\n%s\nFrom: %s\nTo: %s\nSubject: %s' % (uid, date, from_addr, to_addr, subject))
|
||||||
|
imap.logout()
|
||||||
|
|
||||||
|
# Setup CLI options and help menu
|
||||||
|
global_parser = argparse.ArgumentParser(prog="pymailer", description="Simple mail client for sending emails, and checking and manipulating an inbox")
|
||||||
|
subparsers = global_parser.add_subparsers(title="subcommands", help="available subcommands", dest="subcommand", required=True)
|
||||||
|
|
||||||
|
send_mail_parser = subparsers.add_parser("send", help="send an email")
|
||||||
|
send_mail_parser.add_argument('--smtp_host', help="SMTP server hostname", required=True)
|
||||||
|
send_mail_parser.add_argument('--smtp_user', help="SMTP server logon username", required=True)
|
||||||
|
send_mail_parser.add_argument('--smtp_password', help="SMTP server logon password", required=True)
|
||||||
|
send_mail_parser.add_argument('--from_addr', help="Address to send email from", required=True)
|
||||||
|
send_mail_parser.add_argument('--to_addr', help="Address to send email to", required=True)
|
||||||
|
send_mail_parser.add_argument('--subject', help="Email subject", required=True)
|
||||||
|
send_mail_parser.add_argument('--body', help="Email body", required=True)
|
||||||
|
send_mail_parser.add_argument('--debug_level', help="Debug level, higher number is more verbose", type=int, choices=range(0, 3), default=0)
|
||||||
|
send_mail_parser.set_defaults(func=send_mail)
|
||||||
|
|
||||||
|
message_list_parser = subparsers.add_parser("message_list", help="lists all messages in an inbox via IMAP")
|
||||||
|
message_list_parser.add_argument('--imap_host', help="IMAP server hostname", required=True)
|
||||||
|
message_list_parser.add_argument('--imap_user', help="IMAP server logon username", required=True)
|
||||||
|
message_list_parser.add_argument('--imap_password', help="IMAP server logon password", required=True)
|
||||||
|
message_list_parser.add_argument('--folder', help="Inbox folder", default='INBOX')
|
||||||
|
message_list_parser.set_defaults(func=message_list)
|
||||||
|
|
||||||
|
message_search_parser = subparsers.add_parser("message_search", help="search an inbox for messages via IMAP")
|
||||||
|
message_search_parser.add_argument('--imap_host', help="IMAP server hostname", required=True)
|
||||||
|
message_search_parser.add_argument('--imap_user', help="IMAP server logon username", required=True)
|
||||||
|
message_search_parser.add_argument('--imap_password', help="IMAP server logon password", required=True)
|
||||||
|
message_search_parser.add_argument('--folder', help="Inbox folder", default='INBOX')
|
||||||
|
message_search_parser.add_argument('--search_string', help="Search string e.g 'FROM' 'user@example.com'. Space separated", nargs='*', required=True)
|
||||||
|
message_search_parser.set_defaults(func=message_search)
|
||||||
|
|
||||||
|
message_delete_parser = subparsers.add_parser("message_delete", help="deletes messages in an inbox via IMAP")
|
||||||
|
message_delete_parser.add_argument('--imap_host', help="IMAP server hostname", required=True)
|
||||||
|
message_delete_parser.add_argument('--imap_user', help="IMAP server logon username", required=True)
|
||||||
|
message_delete_parser.add_argument('--imap_password', help="IMAP server logon password", required=True)
|
||||||
|
message_delete_parser.add_argument('--folder', help="Inbox folder", default='INBOX')
|
||||||
|
message_delete_parser.add_argument('--search_string', help="Search string for emails that will be deleted e.g 'FROM' 'user@example.com'. Space separated", nargs='*', required=True)
|
||||||
|
message_delete_parser.add_argument('--dry_run', help="List emails that will be deleted, but don't delete them", choices=('true', 'false'), default='false')
|
||||||
|
message_delete_parser.set_defaults(func=message_delete)
|
||||||
|
|
||||||
|
args = global_parser.parse_args()
|
||||||
|
args.func(args)
|
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
imapclient
|
Loading…
x
Reference in New Issue
Block a user