Я начал с smtpd , чтобы обработать очередь почты, проанализировать входящие электронные письма и отправить их обратно получателям (используя smtpdlib.sendmail ). Я переключился на aiosmtpd , так как мне требовалась многопоточная обработка (в то время как smtpd однопоточен и, кроме того, выглядит как прекращенный).
Кстати, я озадачен aiosmtpd управление почтой содержимое конверта , которое кажется гораздо более детализированным, чем раньше, так хорошо, если вам нужна действительно точная настройка, но несколько больше, если вы просто хотите обрабатывать тело без изменения rest.
Например, smtpd process_message метод, который просто необходим data_decode = True параметр для обработки и декодирования тела письма, не касаясь чего-либо, а aiosmtpd HANDLE_data метод кажется неспособным автоматически декодировать почтовый конверт и часто дает исключения со встроенными изображениями, вложениями и т. Д. ...
РЕДАКТИРОВАТЬ добавлены примеры кода, Сначала smtpd: следующий код создаст экземпляр сервера smtp, ожидающего почту через порт 10025 и доставляющий до 10027 через smtplib (оба localhost). Безопасно работать с переменной data (в основном, выполнять подстановку строк, моя цель) для всех видов почты (на основе текста / html, со встроенными изображениями, вложениями ...)
class PROXY_SMTP(smtpd.SMTPServer):
def process_message(self, peer, mailfrom, rcpttos, data, decode_data=True):
server = smtplib.SMTP('localhost', 10027)
server.sendmail(mailfrom, rcpttos, data)
server.quit()
server = PROXY_SMTP(('127.0.0.1', 10025), None)
asyncore.loop()
Предыдущий код работает хорошо, но в виде одного потока (= 1 письмо одновременно), поэтому я переключился на aiosmtpd для одновременной обработки почты. Пример с aiosmtpd будет примерно таким:
class MyHandler:
async def handle_DATA(self, server, session, envelope):
peer = session.peer
mailfrom = envelope.mail_from
rcpttos = envelope.rcpt_tos
data = envelope.content.decode()
server = smtplib.SMTP('localhost', 10027)
server.sendmail(mailfrom, rcpttos, data)
server.quit()
my_handler = MyHandler()
async def main(loop):
my_controller = Controller(my_handler, hostname='127.0.0.1', port=10025)
my_controller.start()
loop = asyncio.get_event_loop()
loop.create_task(main(loop=loop))
try:
loop.run_forever()
Этот код хорошо работает для текстовых электронных писем, но дает исключения при декодировании envelope.content для любой сложной почты (mime-контент, вложения ...)
Как я могу анализировать и декодировать почтовый текст в aiosmtpd, выполнять подстановку строк, как я делал с smtpd, и повторно вводить через smtplib? Спасибо
--- решено ---
Это то, что я получил до сих пор, небольшие изменения все еще необходимы (в основном для отдельной обработки содержимого MIME и "перестройки") ) но это решает мою главную проблему: получать почту в отдельных потоках, выделять место для обработки текста, спать в течение фиксированного количества времени до окончательной доставки . Благодаря тройным ответам и комментариям я нашел правильный путь.
import asyncio
from aiosmtpd.controller import Controller
import smtplib
from email import message_from_bytes
from email.policy import default
class MyHandler:
async def handle_DATA(self, server, session, envelope):
peer = session.peer
mailfrom = envelope.mail_from
rcpttos = envelope.rcpt_tos
message = message_from_bytes(envelope.content, policy=default)
#HERE MAYBE WOULD BE SAFER TO WALK CONTENTS AND PARSE/MODIFY ONLY MAIL BODY, BUT NO SIDE EFFECTS UNTIL NOW WITH MIME, ATTACHMENTS...
messagetostring = message.as_string() ### smtplib.sendmail WANTED BYTES or STRING, NOT email OBJECT.
### HERE HAPPENS TEXT PROCESSING, STRING SUBSTITUTIONS...
### THIS WAS MY CORE NEED, ASYNCWAIT ON EACH THREAD
await asyncio.sleep(15)
server = smtplib.SMTP('localhost', 10027)
server.send_message(mailfrom, rcpttos, messagetostring) ### NEEDED TO INVERT ARGS ORDER
server.quit()
return '250 OK' ### ADDED RETURN
my_handler = MyHandler()
async def main(loop):
my_controller = Controller(my_handler, hostname='127.0.0.1', port=10025)
my_controller.start()
loop = asyncio.get_event_loop()
loop.create_task(main(loop=loop))
try:
loop.run_forever()