sender.py 4.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. # -*- coding: utf-8 -*-
  2. import smtplib
  3. import typing
  4. from email.message import Message
  5. from email.mime.multipart import MIMEMultipart
  6. from tracim.config import CFG
  7. from tracim.lib.utils.logger import logger
  8. from tracim.lib.utils.utils import get_rq_queue
  9. from tracim.lib.utils.utils import get_redis_connection
  10. from tracim.lib.mail_notifier.utils import SmtpConfiguration
  11. def send_email_through(
  12. config: CFG,
  13. sendmail_callable: typing.Callable[[Message], None],
  14. message: Message,
  15. ) -> None:
  16. """
  17. Send mail encapsulation to send it in async or sync mode.
  18. TODO BS 20170126: A global mail/sender management should be a good
  19. thing. Actually, this method is an fast solution.
  20. :param config: system configuration
  21. :param sendmail_callable: A callable who get message on first parameter
  22. :param message: The message who have to be sent
  23. """
  24. if config.EMAIL_PROCESSING_MODE == config.CST.SYNC:
  25. sendmail_callable(message)
  26. elif config.EMAIL_PROCESSING_MODE == config.CST.ASYNC:
  27. redis_connection = get_redis_connection(config)
  28. queue = get_rq_queue(redis_connection, 'mail_sender')
  29. queue.enqueue(sendmail_callable, message)
  30. else:
  31. raise NotImplementedError(
  32. 'Mail sender processing mode {} is not implemented'.format(
  33. config.EMAIL_PROCESSING_MODE,
  34. )
  35. )
  36. class EmailSender(object):
  37. """
  38. Independent email sender class.
  39. To allow its use in any thread, as an asyncjob_perform() call for
  40. example, it has no dependencies on SQLAlchemy nor tg HTTP request.
  41. """
  42. def __init__(
  43. self,
  44. config: CFG,
  45. smtp_config: SmtpConfiguration,
  46. really_send_messages
  47. ) -> None:
  48. self._smtp_config = smtp_config
  49. self.config = config
  50. self._smtp_connection = None
  51. self._is_active = really_send_messages
  52. def connect(self):
  53. if not self._smtp_connection:
  54. log = 'Connecting from SMTP server {}'
  55. logger.info(self, log.format(self._smtp_config.server))
  56. self._smtp_connection = smtplib.SMTP(
  57. self._smtp_config.server,
  58. self._smtp_config.port
  59. )
  60. self._smtp_connection.ehlo()
  61. if self._smtp_config.login:
  62. try:
  63. starttls_result = self._smtp_connection.starttls()
  64. log = 'SMTP start TLS result: {}'
  65. logger.debug(self, log.format(starttls_result))
  66. except Exception as e:
  67. log = 'SMTP start TLS error: {}'
  68. logger.debug(self, log.format(e.__str__()))
  69. if self._smtp_config.login:
  70. try:
  71. login_res = self._smtp_connection.login(
  72. self._smtp_config.login,
  73. self._smtp_config.password
  74. )
  75. log = 'SMTP login result: {}'
  76. logger.debug(self, log.format(login_res))
  77. except Exception as e:
  78. log = 'SMTP login error: {}'
  79. logger.debug(self, log.format(e.__str__()))
  80. logger.info(self, 'Connection OK')
  81. def disconnect(self):
  82. if self._smtp_connection:
  83. log = 'Disconnecting from SMTP server {}'
  84. logger.info(self, log.format(self._smtp_config.server))
  85. self._smtp_connection.quit()
  86. logger.info(self, 'Connection closed.')
  87. def send_mail(self, message: MIMEMultipart):
  88. if not self._is_active:
  89. log = 'Not sending email to {} (service disabled)'
  90. logger.info(self, log.format(message['To']))
  91. else:
  92. self.connect() # Actually, this connects to SMTP only if required
  93. logger.info(self, 'Sending email to {}'.format(message['To']))
  94. self._smtp_connection.send_message(message)
  95. from tracim.lib.mail_notifier.notifier import EmailManager
  96. EmailManager.log_notification(
  97. action=' SENT',
  98. recipient=message['To'],
  99. subject=message['Subject'],
  100. config=self.config,
  101. )