Added first version of the script
This commit is contained in:
@@ -0,0 +1,189 @@
|
|||||||
|
import imaplib
|
||||||
|
import email
|
||||||
|
from email.header import decode_header
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
import paramiko
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# EMAIL CONFIG
|
||||||
|
# =========================
|
||||||
|
IMAP_SERVER = "outlook.office365.com"
|
||||||
|
EMAIL_ACCOUNT = "mailbox@company.com"
|
||||||
|
PASSWORD = "your_password"
|
||||||
|
|
||||||
|
MAIL_FOLDERS = ["Inbox", "Orders"]
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# SFTP CONFIG
|
||||||
|
# =========================
|
||||||
|
SFTP_HOST = "sftp.company.com"
|
||||||
|
SFTP_PORT = 22
|
||||||
|
SFTP_USERNAME = "sftpuser"
|
||||||
|
SSH_KEY_PATH = "/home/youruser/.ssh/id_rsa"
|
||||||
|
SSH_KEY_PASSPHRASE = None
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# RULES
|
||||||
|
# =========================
|
||||||
|
RULES = [
|
||||||
|
{
|
||||||
|
"from_contains": "supplier1@company.com",
|
||||||
|
"to_contains": "orders@yourcompany.com",
|
||||||
|
"remote_path": "/incoming/orders"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from_contains": "reports@vendor.com",
|
||||||
|
"to_contains": "reports@yourcompany.com",
|
||||||
|
"remote_path": "/incoming/reports"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
TEMP_DOWNLOAD = "/tmp/mail_attachments"
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# HELPERS
|
||||||
|
# =========================
|
||||||
|
def ensure_folder(path):
|
||||||
|
Path(path).mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
def clean_subject(subject):
|
||||||
|
if not subject:
|
||||||
|
return "No Subject"
|
||||||
|
parts = decode_header(subject)
|
||||||
|
decoded_subject = ""
|
||||||
|
for part, encoding in parts:
|
||||||
|
if isinstance(part, bytes):
|
||||||
|
decoded_subject += part.decode(encoding or "utf-8", errors="ignore")
|
||||||
|
else:
|
||||||
|
decoded_subject += part
|
||||||
|
return decoded_subject
|
||||||
|
|
||||||
|
def decode_filename(filename):
|
||||||
|
if not filename:
|
||||||
|
return "unknown"
|
||||||
|
parts = decode_header(filename)
|
||||||
|
decoded_name = ""
|
||||||
|
for part, encoding in parts:
|
||||||
|
if isinstance(part, bytes):
|
||||||
|
decoded_name += part.decode(encoding or "utf-8", errors="ignore")
|
||||||
|
else:
|
||||||
|
decoded_name += part
|
||||||
|
return decoded_name
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# SFTP CONNECTION
|
||||||
|
# =========================
|
||||||
|
def create_sftp_connection():
|
||||||
|
try:
|
||||||
|
key = paramiko.RSAKey.from_private_key_file(
|
||||||
|
SSH_KEY_PATH,
|
||||||
|
password=SSH_KEY_PASSPHRASE
|
||||||
|
)
|
||||||
|
transport = paramiko.Transport((SFTP_HOST, SFTP_PORT))
|
||||||
|
transport.connect(username=SFTP_USERNAME, pkey=key)
|
||||||
|
sftp = paramiko.SFTPClient.from_transport(transport)
|
||||||
|
return sftp, transport
|
||||||
|
except Exception as e:
|
||||||
|
print(f"SFTP connection failed: {e}")
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# UPLOAD FILE
|
||||||
|
# =========================
|
||||||
|
def upload_to_sftp(local_file, remote_folder):
|
||||||
|
sftp, transport = create_sftp_connection()
|
||||||
|
if not sftp:
|
||||||
|
print("Skipping upload due to SFTP connection error")
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
filename = os.path.basename(local_file)
|
||||||
|
remote_path = f"{remote_folder}/{filename}"
|
||||||
|
print(f"Uploading {filename} -> {remote_path}")
|
||||||
|
sftp.put(local_file, remote_path)
|
||||||
|
print("Upload successful")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Failed to upload {filename}: {e}")
|
||||||
|
finally:
|
||||||
|
sftp.close()
|
||||||
|
transport.close()
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# PROCESS EMAIL
|
||||||
|
# =========================
|
||||||
|
def process_email(msg):
|
||||||
|
from_addr = msg.get("From", "").lower()
|
||||||
|
to_addr = msg.get("To", "").lower()
|
||||||
|
subject = clean_subject(msg.get("Subject"))
|
||||||
|
|
||||||
|
print("\n====================")
|
||||||
|
print(f"FROM: {from_addr}")
|
||||||
|
print(f"TO: {to_addr}")
|
||||||
|
print(f"SUBJECT: {subject}")
|
||||||
|
|
||||||
|
matching_rule = None
|
||||||
|
for rule in RULES:
|
||||||
|
if rule["from_contains"].lower() in from_addr and rule["to_contains"].lower() in to_addr:
|
||||||
|
matching_rule = rule
|
||||||
|
break
|
||||||
|
|
||||||
|
if not matching_rule:
|
||||||
|
print("No matching rule")
|
||||||
|
return
|
||||||
|
|
||||||
|
remote_folder = matching_rule["remote_path"]
|
||||||
|
|
||||||
|
for part in msg.walk():
|
||||||
|
content_disposition = str(part.get("Content-Disposition"))
|
||||||
|
if "attachment" not in content_disposition.lower():
|
||||||
|
continue
|
||||||
|
|
||||||
|
filename = decode_filename(part.get_filename())
|
||||||
|
ensure_folder(TEMP_DOWNLOAD)
|
||||||
|
local_path = os.path.join(TEMP_DOWNLOAD, filename)
|
||||||
|
with open(local_path, "wb") as f:
|
||||||
|
f.write(part.get_payload(decode=True))
|
||||||
|
print(f"Saved attachment locally: {local_path}")
|
||||||
|
|
||||||
|
upload_to_sftp(local_path, remote_folder)
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# MAIN
|
||||||
|
# =========================
|
||||||
|
def main():
|
||||||
|
try:
|
||||||
|
mail = imaplib.IMAP4_SSL(IMAP_SERVER)
|
||||||
|
mail.login(EMAIL_ACCOUNT, PASSWORD)
|
||||||
|
print("Connected to mailbox")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Failed to connect/login: {e}")
|
||||||
|
return
|
||||||
|
|
||||||
|
for folder in MAIL_FOLDERS:
|
||||||
|
print(f"\nChecking folder: {folder}")
|
||||||
|
status, _ = mail.select(folder)
|
||||||
|
if status.decode() != "OK":
|
||||||
|
print(f"Cannot access folder: {folder}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
status, messages = mail.search(None, "UNSEEN")
|
||||||
|
if status.decode() != "OK":
|
||||||
|
continue
|
||||||
|
|
||||||
|
email_ids = messages[0].split()
|
||||||
|
print(f"Found {len(email_ids)} unread emails")
|
||||||
|
|
||||||
|
for email_id in email_ids:
|
||||||
|
status, msg_data = mail.fetch(email_id, "(RFC822)")
|
||||||
|
if status.decode() != "OK":
|
||||||
|
continue
|
||||||
|
raw_email = msg_data[0][1]
|
||||||
|
msg = email.message_from_bytes(raw_email)
|
||||||
|
process_email(msg)
|
||||||
|
mail.store(email_id, '+FLAGS', '\\Seen')
|
||||||
|
|
||||||
|
mail.logout()
|
||||||
|
print("\nFinished")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user