Compare commits
10 Commits
8e408554d5
...
main
Author | SHA1 | Date | |
---|---|---|---|
f49138614d
|
|||
ec2b4aaa29
|
|||
76b36d6141
|
|||
0ce2c5fb24
|
|||
e54d35a9d7
|
|||
993ca50352
|
|||
936cffd0ed
|
|||
d7d2b86a9f
|
|||
e2fcedf2c9
|
|||
8a06d7b7e8
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
config.json
|
config.json
|
||||||
*.log
|
*.log
|
||||||
|
.vscode/
|
73
main.py
73
main.py
@ -1,3 +1,4 @@
|
|||||||
|
# pylint: disable=C0301, C0114
|
||||||
import json
|
import json
|
||||||
import imaplib
|
import imaplib
|
||||||
import smtplib
|
import smtplib
|
||||||
@ -5,97 +6,113 @@ import email
|
|||||||
from email.mime.multipart import MIMEMultipart
|
from email.mime.multipart import MIMEMultipart
|
||||||
from email.mime.text import MIMEText
|
from email.mime.text import MIMEText
|
||||||
from email.utils import parseaddr
|
from email.utils import parseaddr
|
||||||
|
from email.header import decode_header
|
||||||
import time
|
import time
|
||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
import socks
|
|
||||||
import socket
|
import socket
|
||||||
|
import re
|
||||||
|
import socks
|
||||||
|
|
||||||
from email.header import decode_header
|
def provider_in(address, provider_list):
|
||||||
|
"""Check if the provider is in the provider list"""
|
||||||
|
for provider in provider_list:
|
||||||
|
if address.endswith(provider):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def decode_mime_words(s):
|
def decode_mime_words(s):
|
||||||
|
"""Decodes a string containing multiple MIME words"""
|
||||||
decoded_fragments = decode_header(s)
|
decoded_fragments = decode_header(s)
|
||||||
return ''.join(
|
return ''.join(
|
||||||
str(fragment, encoding or 'utf-8') if isinstance(fragment, bytes) else fragment
|
str(fragment, encoding or 'utf-8') if isinstance(fragment, bytes) else fragment
|
||||||
for fragment, encoding in decoded_fragments
|
for fragment, encoding in decoded_fragments
|
||||||
)
|
)
|
||||||
|
|
||||||
def add_mask(original_msg, content):
|
def add_mask(original_msg, content, is_html):
|
||||||
|
"""Adds a mask to the content"""
|
||||||
original_subject = decode_mime_words(original_msg['Subject'])
|
original_subject = decode_mime_words(original_msg['Subject'])
|
||||||
from_name, from_address = parseaddr(original_msg['From'])
|
from_name, from_address = parseaddr(original_msg['From'])
|
||||||
from_name = decode_mime_words(from_name)
|
from_name = decode_mime_words(from_name)
|
||||||
to_name, to_address = parseaddr(original_msg['To'])
|
to_name, to_address = parseaddr(original_msg['To'])
|
||||||
to_name = decode_mime_words(to_name)
|
to_name = decode_mime_words(to_name)
|
||||||
header = f"""<table align=center style="background:#3d3d3d;padding:8px 16px;margin-top:30px;margin-bottom:30px;width:96%;border-radius:6px;max-width:1200px"width=100% bgcolor=#3D3D3D><tr><td style=font-size:xx-large;font-weight:bolder;color:#fff width=50% align=left>Forwarded Email<td style=color:#fff;text-align:right width=50% align=right><p>From: {from_name} <{from_address}><p>To: {to_name} <{to_address}><p>Subject: {original_subject}</table><table align=center style=padding:0;max-width:850px width=100%><tr><td style=padding-left:15px;padding-right:15px width=100%>"""
|
header = f"""<html><head></head><body><table style="background:#3d3d3d;padding:8px 16px;margin-top:30px;margin-bottom:30px;width:96%;border-radius:6px;max-width:1200px" width="100%" bgcolor="#3D3D3D" align="center"><tbody><tr><td width="50%" align="left" style="font-weight:bolder;color:#fff"><p style="font-size:x-large;margin-block:.2em">Forwarded Email</p>{'' if is_html else '<p style="font-size: medium; margin-block: 0.2em;">This email is plain text, it may have display issues</p>'}</td><td width="50%" align="right" style="color:#fff;text-align:right"><p style="margin-block: 0.1em">From: {from_name} <<a href="mailto:{from_address}">{from_address}</a>></p><p style="margin-block: 0.1em">To: {to_name} <<a href="mailto:{to_address}">{to_address}</a>></p><p style="margin-block:.1em">Subject: {original_subject}</p></td></tr></tbody></table><table style="padding:0;max-width:850px" width="100%" align="center"><tbody><tr><td style="padding-left:15px;padding-right:15px" width="100%">"""
|
||||||
footer = f"""<table align=center bgcolor=#3D3D3D style="background:#3d3d3d;padding:8px 16px;margin-top:30px;margin-bottom:30px;width:96%;border-radius:6px;max-width:1200px"width=100%><tr><td align=left style=color:#d22;font-size:xx-large;font-weight:bolder width=50%>FORWARDED<td align=right style=color:#fff;text-align:left width=50%><p style=font-size:x-large;font-weight:700;margin-block:0>Notice:<p style=margin-block:.1em> This is a automatically forwarded email, which means it may contains something bad.<p style=margin-block:.1em> You shouldn't reply directly to this email, it will never reach your destination!</table>"""
|
footer = """</td></tr></tbody></table><table style="background:#3d3d3d;padding:8px 16px;margin-top:30px;margin-bottom:30px;width:96%;border-radius:6px;max-width:1200px" width="100%" bgcolor="#3D3D3D" align="center"><tbody><tr><td width="50%" align="left" style="color:#d22;font-size:xx-large;font-weight:bolder">FORWARDED</td><td width="50%" align="right" style="color:#fff;text-align:left"><p style="font-size:x-large;font-weight:700;margin-block:0">Notice:</p><p style="margin-block:.1em"> This is a automatically forwarded email, which means it may contains something bad.</p><p style="margin-block:.1em"> You shouldn't reply directly to this email, it will never reach your destination!</p></td></tr></tbody></table></body></html>"""
|
||||||
return header + content + footer
|
return header + content + footer
|
||||||
|
|
||||||
def load_config(config_file='config.json'):
|
def load_config(config_file='config.json'):
|
||||||
with open(config_file, 'r') as file:
|
"""Loads the configuration file"""
|
||||||
|
with open(config_file, 'r', encoding='utf-8') as file:
|
||||||
config = json.load(file)
|
config = json.load(file)
|
||||||
return config
|
return config
|
||||||
|
|
||||||
def set_proxy(proxy_config):
|
def set_proxy(proxy_config):
|
||||||
|
"""Sets the proxy"""
|
||||||
socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, proxy_config['server'], proxy_config['port'])
|
socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, proxy_config['server'], proxy_config['port'])
|
||||||
socket.socket = socks.socksocket
|
socket.socket = socks.socksocket
|
||||||
|
|
||||||
def setup_logging(filename):
|
def setup_logging(filename):
|
||||||
|
"""Sets up the logging"""
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
logger.setLevel('DEBUG')
|
logger.setLevel('DEBUG')
|
||||||
BASIC_FORMAT = "%(asctime)s >> %(levelname)s - %(message)s"
|
basic_format = "%(asctime)s >> %(levelname)s - %(message)s"
|
||||||
DATE_FORMAT = '%Y-%m-%d %H:%M:%S'
|
date_format = '%Y-%m-%d %H:%M:%S'
|
||||||
formatter = logging.Formatter(BASIC_FORMAT, DATE_FORMAT)
|
formatter = logging.Formatter(basic_format, date_format)
|
||||||
chlr = logging.StreamHandler()
|
chlr = logging.StreamHandler()
|
||||||
chlr.setFormatter(formatter)
|
chlr.setFormatter(formatter)
|
||||||
chlr.setLevel('INFO')
|
chlr.setLevel('INFO')
|
||||||
fhlr = logging.FileHandler(filename)
|
fhlr = logging.FileHandler(filename, encoding='utf-8')
|
||||||
fhlr.setFormatter(formatter)
|
fhlr.setFormatter(formatter)
|
||||||
logger.addHandler(chlr)
|
logger.addHandler(chlr)
|
||||||
logger.addHandler(fhlr)
|
logger.addHandler(fhlr)
|
||||||
return logger
|
return logger
|
||||||
|
|
||||||
def get_unforwarded_emails(account_config, logger):
|
def get_unforwarded_emails(account_config, logger):
|
||||||
|
"""Gets the unforwarded emails"""
|
||||||
if account_config['proxy']['enabled']:
|
if account_config['proxy']['enabled']:
|
||||||
set_proxy(account_config['proxy'])
|
set_proxy(account_config['proxy'])
|
||||||
|
|
||||||
if account_config['imap'].get('use_ssl', True):
|
if account_config['imap'].get('use_ssl', True):
|
||||||
imap = imaplib.IMAP4_SSL(account_config['imap']['server'], account_config['imap']['port'])
|
imap = imaplib.IMAP4_SSL(account_config['imap']['server'], account_config['imap']['port'], timeout=10)
|
||||||
else:
|
else:
|
||||||
imap = imaplib.IMAP4(account_config['imap']['server'], account_config['imap']['port'])
|
imap = imaplib.IMAP4(account_config['imap']['server'], account_config['imap']['port'], timeout=10)
|
||||||
|
|
||||||
imap.login(account_config['email'], account_config['password'])
|
imap.login(account_config['email'], account_config['password'])
|
||||||
|
|
||||||
if "163.com" in account_config['email'] or "126.com" in account_config['email']:
|
if provider_in(account_config['email'], ["163.com", "126.com"]):
|
||||||
imaplib.Commands['ID'] = ('AUTH')
|
imaplib.Commands['ID'] = 'AUTH'
|
||||||
args = ("name","XXXX","contact","XXXX@163.com","version","1.0.0","vendor","myclient")
|
args = ("name","XXXX","contact","XXXX@163.com","version","1.0.0","vendor","myclient")
|
||||||
typ, dat = imap._simple_command('ID', '("' + '" "'.join(args) + '")')
|
imap._simple_command('ID', '("' + '" "'.join(args) + '")') # pylint: disable=W0212
|
||||||
|
|
||||||
imap.select()
|
imap.select()
|
||||||
|
|
||||||
status, messages = imap.search(None, 'UNSEEN')
|
_, messages = imap.search(None, 'UNSEEN')
|
||||||
email_ids = messages[0].split()
|
email_ids = messages[0].split()
|
||||||
|
|
||||||
emails = []
|
emails = []
|
||||||
for email_id in email_ids:
|
for email_id in email_ids:
|
||||||
status, data = imap.fetch(email_id, '(RFC822)')
|
_, data = imap.fetch(email_id, '(RFC822)')
|
||||||
for response_part in data:
|
for response_part in data:
|
||||||
if isinstance(response_part, tuple):
|
if isinstance(response_part, tuple):
|
||||||
msg = email.message_from_bytes(response_part[1])
|
msg = email.message_from_bytes(response_part[1]) # pylint: disable=E1136
|
||||||
emails.append((email_id, msg))
|
emails.append((email_id, msg))
|
||||||
|
imap.store(email_id, '+FLAGS', '\\Seen')
|
||||||
|
|
||||||
imap.logout()
|
imap.logout()
|
||||||
if len(emails) > 0:
|
if len(emails) > 0:
|
||||||
logger.info(f"Retrieved {len(emails)} new emails from {account_config['email']}")
|
logger.info(f"Retrieved {len(emails)} new emails from {account_config['email']}")
|
||||||
return emails
|
return emails
|
||||||
|
|
||||||
def forward_emails(account_config, emails, logger):
|
def forward_emails(account_config, emails, logger):
|
||||||
|
"""Forwards the emails"""
|
||||||
if account_config['proxy']['enabled']:
|
if account_config['proxy']['enabled']:
|
||||||
set_proxy(account_config['proxy'])
|
set_proxy(account_config['proxy'])
|
||||||
|
|
||||||
smtp = None
|
smtp = None
|
||||||
if account_config['smtp'].get('use_ssl', False):
|
if account_config['smtp'].get('use_ssl', False):
|
||||||
smtp = smtplib.SMTP_SSL(account_config['smtp']['server'], account_config['smtp']['port'])
|
smtp = smtplib.SMTP_SSL(account_config['smtp']['server'], account_config['smtp']['port'], timeout=10)
|
||||||
else:
|
else:
|
||||||
smtp = smtplib.SMTP(account_config['smtp']['server'], account_config['smtp']['port'])
|
smtp = smtplib.SMTP(account_config['smtp']['server'], account_config['smtp']['port'], timeout=10)
|
||||||
if account_config['smtp'].get('use_starttls', False):
|
if account_config['smtp'].get('use_starttls', False):
|
||||||
smtp.starttls()
|
smtp.starttls()
|
||||||
|
|
||||||
@ -108,8 +125,8 @@ def forward_emails(account_config, emails, logger):
|
|||||||
to_name, to_address = parseaddr(original_msg['To'])
|
to_name, to_address = parseaddr(original_msg['To'])
|
||||||
to_name = decode_mime_words(to_name)
|
to_name = decode_mime_words(to_name)
|
||||||
msg = MIMEMultipart('mixed')
|
msg = MIMEMultipart('mixed')
|
||||||
msg['From'] = f"{from_name} ({from_address}) via Forwarder <{account_config['email']}>"
|
msg['From'] = f'"{from_name} ({from_address}) via Forwarder" <{account_config["email"]}>'
|
||||||
msg['To'] = f"{to_name} ({to_address}) via Forwarder <{recipient}>"
|
msg['To'] = f'"{to_name} ({to_address}) via Forwarder" <{recipient}>'
|
||||||
original_subject = decode_mime_words(original_msg['Subject'])
|
original_subject = decode_mime_words(original_msg['Subject'])
|
||||||
msg['Subject'] = original_subject
|
msg['Subject'] = original_subject
|
||||||
|
|
||||||
@ -133,7 +150,12 @@ def forward_emails(account_config, emails, logger):
|
|||||||
logger.error(f"Failed to extract body from email {email_id}")
|
logger.error(f"Failed to extract body from email {email_id}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
html_content = add_mask(original_msg, body)
|
is_html = bool(re.compile(r'<[^>]+>').search(body))
|
||||||
|
|
||||||
|
if not is_html:
|
||||||
|
body = body.replace('\n', '<br>')
|
||||||
|
|
||||||
|
html_content = add_mask(original_msg, body, is_html)
|
||||||
msg.attach(MIMEText(html_content, 'html'))
|
msg.attach(MIMEText(html_content, 'html'))
|
||||||
|
|
||||||
for attachment in attachments:
|
for attachment in attachments:
|
||||||
@ -149,6 +171,7 @@ def forward_emails(account_config, emails, logger):
|
|||||||
smtp.quit()
|
smtp.quit()
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
"""Main function"""
|
||||||
config = load_config()
|
config = load_config()
|
||||||
logger = setup_logging(config['log'])
|
logger = setup_logging(config['log'])
|
||||||
|
|
||||||
@ -159,8 +182,8 @@ def main():
|
|||||||
emails = get_unforwarded_emails(account, logger)
|
emails = get_unforwarded_emails(account, logger)
|
||||||
if emails:
|
if emails:
|
||||||
forward_emails(account, emails, logger)
|
forward_emails(account, emails, logger)
|
||||||
except Exception as e:
|
except Exception as e: # pylint: disable=W0718
|
||||||
logger.error(f"Error processing account {account['email']}: {str(e)}")
|
logger.error(f"Error processing account {account['email']}: {str(e)}") # pylint: disable=W1203
|
||||||
|
|
||||||
logger.info(datetime.datetime.now().strftime("Check finished at %Y-%m-%d %H:%M:%S"))
|
logger.info(datetime.datetime.now().strftime("Check finished at %Y-%m-%d %H:%M:%S"))
|
||||||
time.sleep(config.get('check_interval', 60))
|
time.sleep(config.get('check_interval', 60))
|
||||||
|
149
template.html
149
template.html
@ -1,71 +1,78 @@
|
|||||||
<table
|
<html>
|
||||||
style="
|
<head></head>
|
||||||
background: #3d3d3d;
|
<body>
|
||||||
padding: 8px 16px;
|
<table
|
||||||
margin-top: 30px;
|
style="
|
||||||
margin-bottom: 30px;
|
background: #3d3d3d;
|
||||||
width: 96%;
|
padding: 8px 16px;
|
||||||
border-radius: 6px;
|
margin-top: 30px;
|
||||||
max-width: 1200px;
|
margin-bottom: 30px;
|
||||||
"
|
width: 96%;
|
||||||
width="100%"
|
border-radius: 6px;
|
||||||
bgcolor="#3D3D3D"
|
max-width: 1200px;
|
||||||
align="center">
|
"
|
||||||
<tbody>
|
width="100%"
|
||||||
<tr>
|
bgcolor="#3D3D3D"
|
||||||
<td
|
align="center">
|
||||||
width="50%"
|
<tbody>
|
||||||
align="left"
|
<tr>
|
||||||
style="
|
<td width="50%" align="left" style="font-weight: bolder; color: #ffffff">
|
||||||
font-size: xx-large;
|
<p style="font-size: x-large; margin-block: 0.2em">Forwarded Email</p>
|
||||||
font-weight: bolder;
|
<!-- <p style="font-size: medium; margin-block: 0.2em;">This email is plain text, it may have display issues</p> -->
|
||||||
color: #ffffff;
|
</td>
|
||||||
">
|
<td width="50%" align="right" style="color: #ffffff; text-align: right">
|
||||||
Forwarded Email
|
<p style="margin-block: 0.1em">From: {from_name} <<a href="mailto:{from_address}">{from_address}</a>></p>
|
||||||
</td>
|
<p style="margin-block: 0.1em">To: {to_name} <<a href="mailto:{to_address}">{to_address}</a>></p>
|
||||||
<td width="50%" align="right" style="color: #ffffff; text-align: right;">
|
<p style="margin-block: 0.1em">Subject: {original_subject}</p>
|
||||||
<p>From: {from_name} <{from_address}></p>
|
</td>
|
||||||
<p>To: {to_name} <{to_address}></p>
|
</tr>
|
||||||
<p>Subject: {original_subject}</p>
|
</tbody>
|
||||||
</td>
|
</table>
|
||||||
</tr>
|
<table style="padding: 0; max-width: 850px" width="100%" align="center">
|
||||||
</tbody>
|
<tbody>
|
||||||
</table>
|
<tr>
|
||||||
<table style="padding: 0; max-width: 850px" width="100%" align="center">
|
<td style="padding-left: 15px; padding-right: 15px" width="100%">
|
||||||
<tbody>
|
<!-- content -->
|
||||||
<tr>
|
</td>
|
||||||
<td style="padding-left: 15px; padding-right: 15px" width="100%">
|
</tr>
|
||||||
<!-- content -->
|
</tbody>
|
||||||
</td>
|
</table>
|
||||||
</tr>
|
<table
|
||||||
</tbody>
|
style="
|
||||||
</table>
|
background: #3d3d3d;
|
||||||
<table
|
padding: 8px 16px;
|
||||||
style="
|
margin-top: 30px;
|
||||||
background: #3d3d3d;
|
margin-bottom: 30px;
|
||||||
padding: 8px 16px;
|
width: 96%;
|
||||||
margin-top: 30px;
|
border-radius: 6px;
|
||||||
margin-bottom: 30px;
|
max-width: 1200px;
|
||||||
width: 96%;
|
"
|
||||||
border-radius: 6px;
|
width="100%"
|
||||||
max-width: 1200px;
|
bgcolor="#3D3D3D"
|
||||||
"
|
align="center">
|
||||||
width="100%"
|
<tbody>
|
||||||
bgcolor="#3D3D3D"
|
<tr>
|
||||||
align="center">
|
<td
|
||||||
<tbody>
|
width="50%"
|
||||||
<tr>
|
align="left"
|
||||||
<td
|
style="color: #dd2222; font-size: xx-large; font-weight: bolder">
|
||||||
width="50%"
|
FORWARDED
|
||||||
align="left"
|
</td>
|
||||||
style="color: #dd2222; font-size: xx-large; font-weight: bolder">
|
<td width="50%" align="right" style="color: #ffffff; text-align: left">
|
||||||
FORWARDED
|
<p style="font-size: x-large; font-weight: bold; margin-block: 0">
|
||||||
</td>
|
Notice:
|
||||||
<td width="50%" align="right" style="color: #ffffff; text-align: left;">
|
</p>
|
||||||
<p style="font-size: x-large; font-weight: bold; margin-block: 0;">Notice:</p>
|
<p style="margin-block: 0.1em">
|
||||||
<p style="margin-block: 0.1em;"> This is a automatically forwarded email, which means it may contains something bad.</p>
|
 This is a automatically forwarded email, which means it may
|
||||||
<p style="margin-block: 0.1em;"> You shouldn't reply directly to this email, it will never reach your destination!</p>
|
contains something bad.
|
||||||
</td>
|
</p>
|
||||||
</tr>
|
<p style="margin-block: 0.1em">
|
||||||
</tbody>
|
 You shouldn't reply directly to this email, it will never reach
|
||||||
</table>
|
your destination!
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
Reference in New Issue
Block a user