自动化办公--定时发送邮件

自动化脚本案例



一、实现功能

1. 邮件附件处理:包含常用文件格式的自动识别,或者自己指定处理方式。
2. 发送邮件
3. 指定时间发送邮件 或 延时发送邮件。
4. 从文件中导入邮件正文。

二、具体实现

1. 附件处理

这个函数的作用就是添加文件到 发送的邮件中。
不同格式的文件被处理成附件的方式有所不同。
txt、md…格式属于文本类型,doc,xlsx,pdf这类数据二进制类型。

file_type这个参数就是为了解决这个问题,为None是,函数自动推断文件格式,根据预设的去处理,但是没有包含所有的文件类型。
file_type取0代表文本文件,取1代表二进制文件

"""
    @func:  返回邮箱附件对象
    @param: 
        file_path: 文件路径
        file_type: 文件类型
            0:文本文件
            1:二进制文件
    @return:   附件对象
    @author: liuhuan
    @date: 2024年7月26日
"""
def create_attachment(file_path, file_type=None):
    # 获取文件名和扩展名
    file_name = file_path.split('/')[-1]
    file_extension = file_name.split('.')[-1].lower()

    # 创建邮件对象
    message = MIMEMultipart()

    # 定义 MIME 类型映射
    text_types = ['txt', 'md', 'csv', 'json', 'xml']
    binary_types = {
        'pdf': 'application/pdf',
        'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
        'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
        'pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
        'jpg': 'image/jpeg',
        'jpeg': 'image/jpeg',
        'png': 'image/png',
        'gif': 'image/gif',
        'zip': 'application/zip',
        'tar': 'application/x-tar',
    }

    try:
        if file_type is None:
            if file_extension in text_types:
                with open(file_path, 'r', encoding='utf-8') as file:
                    att = MIMEText(file.read(), _subtype=file_extension, _charset='utf-8')
                    att.add_header('Content-Disposition', 'attachment', filename=file_name)
                    message.attach(att)
            elif file_extension in binary_types:
                mime_type = binary_types[file_extension]
                with open(file_path, 'rb') as file:
                    att = MIMEApplication(file.read(), _subtype=mime_type.split('/')[-1])
                    att.add_header('Content-Disposition', 'attachment', filename=file_name)
                    message.attach(att)
            else:
                raise ValueError(f"不支持的文件格式: {file_extension}")
        elif file_type == 0:  # 文本文件
            if file_extension in text_types:
                with open(file_path, 'r', encoding='utf-8') as file:
                    att = MIMEText(file.read(), _subtype=file_extension, _charset='utf-8')
                    att.add_header('Content-Disposition', 'attachment', filename=file_name)
                    message.attach(att)
            else:
                raise ValueError(f"指定的文件类型为文本,但文件格式不支持: {file_extension}")
        elif file_type == 1:  # 二进制文件
            if file_extension in binary_types:
                mime_type = binary_types[file_extension]
                with open(file_path, 'rb') as file:
                    att = MIMEApplication(file.read(), _subtype=mime_type.split('/')[-1])
                    att.add_header('Content-Disposition', 'attachment', filename=file_name)
                    message.attach(att)
            else:
                raise ValueError(f"指定的文件类型为二进制,但文件格式不支持: {file_extension}")
        else:
            raise ValueError("无效的 file_type 参数")
    except Exception as e:
        print(f"创建附件时发生错误: {e}")
        return None

    return message

2. 定时发送、延时发送

可发送一个以上附件给一名及以上接收者

没啥好介绍的,直接看注释就可以了。
有一个小缺陷,这个函数无法指定创建附件对象 的文件类型(上一小节函数的 filetype参数)。可以使用一个全局变量替换调用处的直接赋值。

"""
    @func:  可发送一个以上附件给一名及以上接收者
    @param: 
        send:发送者的邮箱和授权密码,字典格式,
        recv:接收者的邮箱列表,list
        email_info:邮件主题和内容,字典格式
        filenames:附件文件名列表,list
        flag:定时类型标志位,0:定时发送,1:延迟发送
        time_param:时间参数,定时发送时为发送时间,延迟发送时为延迟时间
    @return:   无返回值
    @author: liuhuan
    @date: 2024年7月26日
"""
def send_email(send, recv, email_info, filenames, flag, time_param):
	# 发送者、接受者、邮件数据解析
    sender, password = send["sender"], send["password"]
    recvers = recv  # 现在 recvers 是一个列表
    subject, content = email_info["subject"], email_info["content"]

    # 创建邮件对象
    message = MIMEMultipart()
    message.attach(MIMEText(content, "plain", "utf-8"))  # 邮件正文

    # 添加附件,
    if filenames:
        for filename in filenames:
            file_type = None  # 默认为自动判断文件类型
            if len(filename) == 1:
                file_path = filename[0]
            else:
                file_path, file_type = filename
            att = create_attachment(file_path,file_type)  # 调用 create_attachment 函数创建附件
            if att:
                message.attach(att)

    message['Subject'] = subject  # 邮件标题
    message['From'] = sender  # 发件人

    def send_now():
        try:
            smtp = smtplib.SMTP_SSL(email_server, email_port)  # 实例化 smtp 服务器
            smtp.login(sender, password)  # 发件人登录
            for recver in recvers:
                message['To'] = recver  # 收件人
                smtp.sendmail(sender, [recver], message.as_string())  # 发送邮件
                print(f"邮件发送成功给 {recver}")
            smtp.close()
        except Exception as e:
            print(f"发送邮件时发生错误: {e}")

    if flag == 0:  # 定时发送
        schedule.every().day.at(time_param).do(send_now)
        print(f"定时任务已设置,每天 {time_param} 发送邮件。")
        scheduler_thread = threading.Thread(target=start_scheduler)
        scheduler_thread.start()
    elif flag == 1:  # 延迟发送
        def delayed_send():
            if type(time_param) == int:
                time.sleep(time_param)
            elif type(time_param) == str:
                if ":" in time_param:
                    h,s,m = map(int,time_param.split(":"))
                if ":" in time_param:
                    h,s,m = map(int,time_param.split(":"))
                time.sleep(h*3600+s*60+m)
            send_now()
        
        thread = threading.Thread(target=delayed_send)
        thread.start()
        print(f"邮件将在 {time_param} 秒后发送。")
    else:
        print("无效的 flag 参数")


def start_scheduler():
    while True:
        schedule.run_pending()
        time.sleep(1)

3. 从文件初始化邮件附件

就是将文件读出付给 content变量。
这个函数太简单了,没有包含常用的doc文档之类的,只支持txt文件,这种文本文件。有需要可以自己丰富一下。

"""
    @ func:从文件初始化邮件内容
    @ param:
        filename:文件名
        subject:邮件主题,默认为文件名
    @ return:邮件内容字典,包含主题和内容
    @ update: 2024年7月26日
"""
def inti_email_from_file(filename,subject=None):
    if not subject:
        subject = filename.split("/")[-1].split(".")[0]
    content = open(filename, "r", encoding="utf-8").read()
    return {
        "subject":subject,
        "content":content}

三、完整代码

import smtplib #smtp服务器
from email.mime.text import MIMEText #邮件文本
from email.mime.multipart import MIMEMultipart #邮件附件
from email.mime.application import MIMEApplication
import schedule
import time
import threading
from datetime import datetime, timedelta

"""
    @func:  返回邮箱附件对象
    @param: 
        file_path: 文件路径
        file_type: 文件类型
            0:文本文件
            1:二进制文件
    @return:   附件对象
    @author: liuhuan
    @date: 2024年7月26日
"""
def create_attachment(file_path, file_type=None):
    # 获取文件名和扩展名
    file_name = file_path.split('/')[-1]
    file_extension = file_name.split('.')[-1].lower()

    # 创建邮件对象
    message = MIMEMultipart()
    # 定义 MIME 类型映射
    text_types = ['txt', 'md', 'csv', 'json', 'xml']
    binary_types = {
        'pdf': 'application/pdf',
        'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
        'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
        'pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
        'jpg': 'image/jpeg',
        'jpeg': 'image/jpeg',
        'png': 'image/png',
        'gif': 'image/gif',
        'zip': 'application/zip',
        'tar': 'application/x-tar',
    }

    try:
        if file_type is None:
            if file_extension in text_types:
                with open(file_path, 'r', encoding='utf-8') as file:
                    att = MIMEText(file.read(), _subtype=file_extension, _charset='utf-8')
                    att.add_header('Content-Disposition', 'attachment', filename=file_name)
                    message.attach(att)
            elif file_extension in binary_types:
                mime_type = binary_types[file_extension]
                with open(file_path, 'rb') as file:
                    att = MIMEApplication(file.read(), _subtype=mime_type.split('/')[-1])
                    att.add_header('Content-Disposition', 'attachment', filename=file_name)
                    message.attach(att)
            else:
                raise ValueError(f"不支持的文件格式: {file_extension}")
        elif file_type == 0:  # 文本文件
            if file_extension in text_types:
                with open(file_path, 'r', encoding='utf-8') as file:
                    att = MIMEText(file.read(), _subtype=file_extension, _charset='utf-8')
                    att.add_header('Content-Disposition', 'attachment', filename=file_name)
                    message.attach(att)
            else:
                raise ValueError(f"指定的文件类型为文本,但文件格式不支持: {file_extension}")
        elif file_type == 1:  # 二进制文件
            if file_extension in binary_types:
                mime_type = binary_types[file_extension]
                with open(file_path, 'rb') as file:
                    att = MIMEApplication(file.read(), _subtype=mime_type.split('/')[-1])
                    att.add_header('Content-Disposition', 'attachment', filename=file_name)
                    message.attach(att)
            else:
                raise ValueError(f"指定的文件类型为二进制,但文件格式不支持: {file_extension}")
        else:
            raise ValueError("无效的 file_type 参数")
    except Exception as e:
        print(f"创建附件时发生错误: {e}")
        return None

    return message

    # send:发送者的邮箱和授权密码
    # recv:接收者的邮箱列表
    # email_info:邮件主题和内容
    # filenames:附件文件名列表
    # flag:时间标志位,0:定时发送,1:延迟发送
    # time_param:时间参数,定时发送时为发送时间,延迟发送时为延迟时间

"""
    @func:  可发送一个以上附件给一名及以上接收者
    @param: 
        end:发送者的邮箱和授权密码,字典格式,
        recv:接收者的邮箱列表,list
        email_info:邮件主题和内容,字典格式
        filenames:附件文件名列表,list
        flag:时间标志位,0:定时发送,1:延迟发送
        time_param:时间参数,定时发送时为发送时间,延迟发送时为延迟时间
    @return:   无返回值
    @author: liuhuan
    @date: 2024年7月26日
"""
def send_email(send, recv, email_info, filenames, flag, time_param):
    sender, password = send["sender"], send["password"]
    recvers = recv  # 现在 recvers 是一个列表
    subject, content = email_info["subject"], email_info["content"]


    # 创建邮件对象
    message = MIMEMultipart()
    message.attach(MIMEText(content, "plain", "utf-8"))  # 邮件正文

    # 添加附件,
    if filenames:
        for filename in filenames:
            file_type = None  # 默认为自动判断文件类型
            if len(filename) == 1:
                file_path = filename[0]
            else:
                file_path, file_type = filename
            att = create_attachment(file_path,file_type)  # 调用 create_attachment 函数创建附件
            if att:
                message.attach(att)

    message['Subject'] = subject  # 邮件标题
    message['From'] = sender  # 发件人

    def send_now():
        try:
            smtp = smtplib.SMTP_SSL(email_server, email_port)  # 实例化 smtp 服务器
            smtp.login(sender, password)  # 发件人登录
            for recver in recvers:
                message['To'] = recver  # 收件人
                smtp.sendmail(sender, [recver], message.as_string())  # 发送邮件
                print(f"邮件发送成功给 {recver}")
            smtp.close()
        except Exception as e:
            print(f"发送邮件时发生错误: {e}")

    if flag == 0:  # 定时发送
        schedule.every().day.at(time_param).do(send_now)
        print(f"定时任务已设置,每天 {time_param} 发送邮件。")
        scheduler_thread = threading.Thread(target=start_scheduler)
        scheduler_thread.start()
    elif flag == 1:  # 延迟发送
        def delayed_send():
            if type(time_param) == int:
                time.sleep(time_param)
            elif type(time_param) == str:
                if ":" in time_param:
                    h,s,m = map(int,time_param.split(":"))
                if ":" in time_param:
                    h,s,m = map(int,time_param.split(":"))
                time.sleep(h*3600+s*60+m)
            send_now()
        
        thread = threading.Thread(target=delayed_send)
        thread.start()
        print(f"邮件将在 {time_param} 秒后发送。")
    else:
        print("无效的 flag 参数")

# 不懂
def start_scheduler():
    while True:
        schedule.run_pending()
        time.sleep(1)

"""
    @ func:从文件初始化邮件内容
    @ param:
        filename:文件名
        subject:邮件主题,默认为文件名
    @ return:邮件内容字典,包含主题和内容
    @ update: 2024年7月26日
"""
def inti_email_from_file(filename,subject=None):
    if not subject:
        subject = filename.split("/")[-1].split(".")[0]
    content = open(filename, "r", encoding="utf-8").read()
    return {
        "subject":subject,
        "content":content}
if __name__ == "__main__":
    # 从字典初始化user_info
    send_info={
        "sender":"发送者@163.com",
        "password":"xxxxxx"
    }
    recv_info=['接受者1@qq.com','接受者2.1@outlook.com']
    
    # 从文件中导入邮件的正文和主题
    email_info= inti_email_from_file("test.txt")

    # 附件文件路径的列表,以字典的形式存放
    # 前者是文件路径,后者是文件类型,0:文本类型, 1:二进制类型
    filenames = [("用户推荐商品表.xlsx",),("用户推荐商品表.xlsx",1)]

    # 不同的邮箱使用不同的服务器和端口,默认都是使用SSL的方式。
    email_server = "smtp.163.com"
    email_port = 994

    send_type = 1  # 定时,0;延迟,1

    send_email(send_info, recv_info, email_info, filenames, send_type, "00:00:00")  # 发送邮件

总结

略。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值