| 61 | |
| 62 | |
| 63 | class MailSender: |
| 64 | def __init__( |
| 65 | self, |
| 66 | smtphost: str = "localhost", |
| 67 | mailfrom: str = "scrapy@localhost", |
| 68 | smtpuser: str | None = None, |
| 69 | smtppass: str | None = None, |
| 70 | smtpport: int = 25, |
| 71 | smtptls: bool = False, |
| 72 | smtpssl: bool = False, |
| 73 | debug: bool = False, |
| 74 | ): |
| 75 | if is_reactorless(): # pragma: no cover |
| 76 | raise RuntimeError(f"{type(self).__name__} requires a Twisted reactor.") |
| 77 | self.smtphost: str = smtphost |
| 78 | self.smtpport: int = smtpport |
| 79 | self.smtpuser: bytes | None = _to_bytes_or_none(smtpuser) |
| 80 | self.smtppass: bytes | None = _to_bytes_or_none(smtppass) |
| 81 | self.smtptls: bool = smtptls |
| 82 | self.smtpssl: bool = smtpssl |
| 83 | self.mailfrom: str = mailfrom |
| 84 | self.debug: bool = debug |
| 85 | |
| 86 | @classmethod |
| 87 | def from_crawler(cls, crawler: Crawler) -> Self: # pragma: no cover |
| 88 | settings = crawler.settings |
| 89 | return cls( |
| 90 | smtphost=settings["MAIL_HOST"], |
| 91 | mailfrom=settings["MAIL_FROM"], |
| 92 | smtpuser=settings["MAIL_USER"], |
| 93 | smtppass=settings["MAIL_PASS"], |
| 94 | smtpport=settings.getint("MAIL_PORT"), |
| 95 | smtptls=settings.getbool("MAIL_TLS"), |
| 96 | smtpssl=settings.getbool("MAIL_SSL"), |
| 97 | ) |
| 98 | |
| 99 | def send( |
| 100 | self, |
| 101 | to: str | list[str], |
| 102 | subject: str, |
| 103 | body: str, |
| 104 | cc: str | list[str] | None = None, |
| 105 | attachs: Sequence[tuple[str, str, IO[Any]]] = (), |
| 106 | mimetype: str = "text/plain", |
| 107 | charset: str | None = None, |
| 108 | _callback: Callable[..., None] | None = None, |
| 109 | ) -> Deferred[None] | None: |
| 110 | from twisted.internet import reactor |
| 111 | |
| 112 | msg: MIMEBase = ( |
| 113 | MIMEMultipart() if attachs else MIMENonMultipart(*mimetype.split("/", 1)) |
| 114 | ) |
| 115 | |
| 116 | to = list(arg_to_iter(to)) |
| 117 | cc = list(arg_to_iter(cc)) |
| 118 | |
| 119 | msg["From"] = self.mailfrom |
| 120 | msg["To"] = COMMASPACE.join(to) |
no outgoing calls