Przeglądaj źródła

feat: 发送邮件

SongZihuan 2 lat temu
rodzic
commit
499880593f
5 zmienionych plików z 155 dodań i 1 usunięć
  1. 1 1
      templates/index/index.html
  2. 24 0
      templates/sender/sender.html
  3. 3 0
      web/__init__.py
  4. 112 0
      web/sender.py
  5. 15 0
      web/user.py

+ 1 - 1
templates/index/index.html

@@ -7,7 +7,7 @@
 
     <div class="btn-group mt-5">
         <a class="btn btn-danger" href="{{ url_for("mailbox.mail_list_page") }}"> 查看邮箱 </a>
-        <a class="btn btn-success"> 发送邮件 </a>
+        <a class="btn btn-success" href="{{ url_for("sender.send_page") }}"> 发送邮件 </a>
     </div>
 
 </div>

+ 24 - 0
templates/sender/sender.html

@@ -0,0 +1,24 @@
+{% extends "base.html" %}
+{% block title %} 发送邮件 {% endblock %}
+
+{% block content %}
+<div class="container">
+    <form method="post" action="{{ url_for("sender.send_page") }}" class="was-validated">
+        {{ form.hidden_tag() }}
+
+        {{ render_field(form.sender_name) }}
+        {{ render_field(form.sender_address) }}
+        {{ render_field(form.subject) }}
+
+        {{ render_field(form.rc) }}
+        {{ render_field(form.cc) }}
+        {{ render_field(form.bcc) }}
+
+        {{ render_text_field(form.content) }}
+
+        <div class="text-end">
+            {{ form.submit(class='btn btn-success me-2') }}
+        </div>
+    </form>
+</div>
+{% endblock %}

+ 3 - 0
web/__init__.py

@@ -41,6 +41,9 @@ class HuamMailFlask(Flask):
         from .mailbox import mailbox
         self.register_blueprint(mailbox, url_prefix="/mailbox")
 
+        from .sender import sender
+        self.register_blueprint(sender, url_prefix="/sender")
+
     def profile_setting(self):
         if conf["DEBUG_PROFILE"]:
             self.wsgi_app = ProfilerMiddleware(self.wsgi_app, sort_by=("cumtime",))

+ 112 - 0
web/sender.py

@@ -0,0 +1,112 @@
+from flask import Blueprint, render_template, flash, redirect, url_for
+from flask_wtf import FlaskForm
+from wtforms import (StringField,
+                     SubmitField,
+                     TextAreaField,
+                     ValidationError)
+from wtforms.validators import DataRequired, Regexp
+from flask_login import login_required, current_user
+import re
+import smtplib
+
+from sender.email import Email
+
+sender = Blueprint("sender", __name__)
+
+
+class MailInputForm(FlaskForm):
+    sender_name = StringField("发信人", description="发信人名称", validators=[DataRequired(f"必须填写发信人名称")])
+    sender_address = StringField("发信地址",
+                                 description="发信人邮件地址",
+                                 validators=[
+                                     DataRequired(f"必须填写发信人名称"),
+                                     Regexp(
+                                         r"^[a-zA-Z0-9_\.\-]+@[a-zA-Z0-9_\-]+(\.[a-zA-Z0-9_\.]+)+$",
+                                         message=f"发信人邮件地址不满足正则表达式")])
+    subject = StringField("标题", description="邮件标题", validators=[DataRequired(f"必须填写邮件标题")])
+
+    rc = StringField("收件人", description="收件人")
+    cc = StringField("抄送人", description="抄送人")
+    bcc = StringField("密送人", description="密送人")
+
+    content = TextAreaField("普通邮件正文", description="邮件正文")
+
+    submit = SubmitField("发送")
+
+    __email = re.compile(r"^[a-zA-Z0-9_.\-]+@[a-zA-Z0-9_\-]+(\.[a-zA-Z0-9_.]+)+$")
+
+    def __init__(self):
+        super(MailInputForm, self).__init__()
+        self.sender_name.data = current_user.username
+        self.sender_address.data = current_user.smtp_address
+
+        self.rc_ = []
+        self.cc_ = []
+        self.bcc_ = []
+
+    def validate_sender_address(self, field):
+        if not MailInputForm.__email.match(field.data):
+            raise ValidationError("邮件地址不合法")
+
+    @staticmethod
+    def __split_address(address: str):
+        res = [i.strip() for i in address.split(" ") if len(i.strip()) > 0]
+        res_len = len(res)
+
+        if res_len != 1 and res_len != 2:
+            return None
+        elif res_len == 1:
+            if not MailInputForm.__email.match(res[0]):
+                raise ValidationError(f"收件地址 {res[0]} 不合法")
+            name = res[0].split("@")[0]
+            return name, res[0]
+        else:
+            if not MailInputForm.__email.match(res[1]):
+                raise ValidationError(f"收件地址 {res[1]} 不合法")
+            return tuple(res)
+
+    def validate_rc(self, field):
+        split = [i.strip() for i in field.data.split(";")]
+        for i in split:
+            res = self.__split_address(i)
+            if res:
+                self.rc_.append(res)
+
+    def validate_cc(self, field):
+        split = [i.strip() for i in field.data.split(";")]
+        for i in split:
+            res = self.__split_address(i)
+            if res:
+                self.cc_.append(res)
+
+    def validate_bcc(self, field):
+        split = [i.strip() for i in field.data.split(";")]
+        for i in split:
+            res = self.__split_address(i)
+            if res:
+                self.bcc_.append(res)
+
+
+@sender.route("/", methods=["GET", "POST"])
+@login_required
+def send_page():
+    form: MailInputForm = MailInputForm()
+    if form.validate_on_submit():
+        email = Email(from_addr=(form.sender_name.data, form.sender_address.data),
+                      subject=form.subject.data,
+                      to_addr=form.rc_,
+                      cc_addr=form.cc_,
+                      bcc_addr=form.bcc_)
+        email.add_text(form.content.data)
+
+        if len(form.rc_) + len(form.cc_) + len(form.bcc_) <= 0:
+            flash("没有收件人")
+        else:
+            try:
+                current_user.send(email)
+            except smtplib.SMTPException:
+                flash("发信失败")
+            else:
+                flash("发信成功")
+        return redirect(url_for("sender.send_page"))
+    return render_template("sender/sender.html", form=form)

+ 15 - 0
web/user.py

@@ -1,4 +1,5 @@
 from sender.smtp import Sender
+from sender.email import Email
 from mailbox.imap import Imap
 from mailbox.email import Mail
 from .db import redis
@@ -16,6 +17,10 @@ class User(UserMixin):
         if passwd:
             redis.hmset(f"user:{username}", {"passwd": passwd})
 
+    @property
+    def smtp_address(self):
+        return conf["SMTP_USERNAME"].format(self.username)
+
     def check_login(self):
         imap = Imap(user=conf["IMAP_USERNAME"].format(self.username),
                     passwd=conf["IMAP_PASSWD"].format(self.passwd),
@@ -115,3 +120,13 @@ class User(UserMixin):
             return None
         return Mail(str(mail_id), byte.encode("utf-8"))
 
+
+    def send(self, email: Email):
+        sender = Sender(user=conf["SMTP_USERNAME"].format(self.username),
+                        passwd=conf["SMTP_PASSWD"].format(self.passwd),
+                        host=conf["SMTP_HOST"],
+                        port=conf["SMTP_PORT"],
+                        ssl=conf["SMTP_SSL"],
+                        start_ssl=conf["SMTP_START_SSL"],
+                        debug=False)
+        sender.send(email)