朋友的小需求-邮件群发工具
侧边栏壁纸
  • 累计撰写 62 篇文章
  • 累计收到 47 条评论

朋友的小需求-邮件群发工具

Skycyan
2023-06-09 / 0 评论 / 70 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2023年06月10日,已超过713天没有更新,若内容或图片失效,请留言反馈。

python群发邮件

需求说明

  1. 通过界面给不同人发送不同的主题、正文、附件的邮件
  2. 支持多线程
  3. 支持日志导出
  4. 支持execl导入

    导入模板下载

2023-6-10 BUG修复

导入后不能在界面上重新选择附件

实现代码

import email
import tkinter as tk
from email import encoders
from tkinter import messagebox, filedialog
import smtplib
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import openpyxl
import threading
import mimetypes
import base64
import os
import functools

b64str = (f'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Vv/uu1b/7rtW5e67VrPuulZw7rtWIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO67VgLuu1YU7rpWJu67VjLuu1Y27btVMO66ViLuu1YMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////8AAP//4AP//wAA//8AAH//AAD//A/4H/8AAP/4fA8H/wAA/+HgAcP/AAD/w4AAcf8AAP+GAAAYfwAA/4wAAAw/AAD9H+AAAj8AAPoYPgABHwAA/hADgAGPAAD0MADAAM8AAOwwAGAPxwAA6DAAMD/nAADYMAAYf+MAANgwAA//8wAA0DAAD//xAACQMAAH//EAAJAQAAf/+QAAsBgAB//5AACwGAAD//kAALAMAAP/+QAAsAwAA//5AACwBgAD//kAALAHAAP/+QAAkAOAA//5AACQA+AD//kAAJgD/n//+QAAmAf////xAADIB/////MAAMwP////8wAAxB/////jAADmP////+cAAOM/////xwAA8z/////PAADxv////48AAPj/////HwAA/H////4/AAD+P////H8AAP8f///4/wAA/4////H/AAD/w///w/8AAP/g//8H/wAA//gf+B//AAD//gAAf/8AAP//wAP//wAA////////AAA=')
subtype = ""
user_name = ""
passwd = ""
Entry1 = None  # 声明Entry1为全局变量
Entry2 = None  # 声明Entry2为全局变量
child_window = None  # 声明child_window为全局变量
rows = []

def windows_configure(event):
    max_width = 1300  # 设置最大宽度为1111
    if event.width > max_width:
        window.geometry(f"{max_width}x{event.height}")  # 调整窗口宽度为最大宽度

def check_scrollbar():
    canvas_height = CAN1.winfo_height()
    content_height = CAN1.bbox("all")[3]
    if content_height <= canvas_height:
        window.unbind("<MouseWheel>")
    else:
        window.bind("<MouseWheel>", on_mouse_wheel)

def on_mouse_wheel(event):
    if CAN1.yview() != (0.0, 1.0):
        CAN1.yview_scroll(-1 * (event.delta // 120), "units")



def msg_1():
    messagebox.showinfo("关于", "作者:杨绪言\n打酱油:Skycyan\n技术支持:ChatGPT")

def log_exp():
    log = output_text.get("1.0","end")
    with open("日志记录.txt", "w") as file:
        file.write(log)
    messagebox.showinfo("提示", "保存成功")

def checkbox_changed():
    global checkbox_var, Entry1, Entry2, user_name, passwd, child_window
    if checkbox_var.get() == 1:
        user_name = Entry1.get()
        passwd = Entry2.get()
        # 导出为config.sc文件
        with open("config.sc", "w") as file:
            file.write(f"username:{user_name}\n")
            file.write(f"passwd:{passwd}\n")
        messagebox.showinfo("提示", "保存成功")
        # 关闭上层窗口
        child_window.destroy()
    else:
        messagebox.showinfo('提示', "未勾选保存复选框,仅本次有效")
        user_name = Entry1.get()
        passwd = Entry2.get()

def user_info_window():
    global Entry1, Entry2, child_window, checkbox_var,user_name,passwd,left,top
    child_window = tk.Toplevel(window)
    child_window.title("设置邮件账户")
    child_window.geometry("350x150+%d+%d" % (left+400, top+150))
    child_window.resizable(0, 0)
    Label1 = tk.Label(child_window, text="发件人地址:")
    Label1.grid(row=1, column=1, ipadx=5, pady=10)
    Entry1 = tk.Entry(child_window, relief="solid")
    Entry1.grid(row=1, column=2)
    Label2 = tk.Label(child_window, text="密码/授权码:")
    Label2.grid(row=2, column=1, ipadx=5, pady=10)
    Entry2 = tk.Entry(child_window, show="*",relief="solid")
    Entry2.grid(row=2, column=2)
    user_button = tk.Button(child_window, text="保存", command=checkbox_changed)
    user_button.grid(row=3, column=2, ipadx=50, padx=5, pady=10)
    checkbox_var = tk.IntVar()
    checkbox1 = tk.Checkbutton(child_window, text="保存用户密码", variable=checkbox_var)
    checkbox1.grid(row=3, column=1, ipadx=5, padx=30, pady=10)
    child_window.grab_set()
    read_config_file()

def read_config_file():
    try:
        global user_name, passwd, Entry1, Entry2,child_window
        with open("config.sc", "r") as file:
            lines = file.readlines()
            if len(lines) >= 2:
                user_name = lines[0].split(":")[1].strip()
                passwd = lines[1].split(":")[1].strip()
                if Entry1 != None:
                    Entry1.insert(tk.END,user_name)
                    Entry2.insert(tk.END,passwd)
            else:
                None
    except:
        None

def on_configure(event):
    CAN1.configure(scrollregion=CAN1.bbox("all"))

def add_rows(recipient="", subject="", message_body="", attachment=""):
    global rows
    row = len(rows)
    Label0 = tk.Label(frame1, text=(row+1), bg="white")  # 此处序号
    Label0.grid(row=row, column=0, ipady=12)
    Label1 = tk.Label(frame1, text=("收件人地址:"), bg="white")  # 此处序号需要加1
    Label1.grid(row=row, column=1, ipady=12)
    Entry1 = tk.Entry(frame1, relief="solid", bg="white")
    Entry1.grid(row=row, column=2, ipady=12)
    Label2 = tk.Label(frame1, text="主题:", bg="white")
    Label2.grid(row=row, column=3, ipady=12)
    Entry2 = tk.Entry(frame1, relief="solid")
    Entry2.grid(row=row, column=4, ipady=12)
    Label3 = tk.Label(frame1, text="正文:", bg="white")
    Label3.grid(row=row, column=5, ipady=12)
    Text1 = tk.Text(frame1, relief="solid", height=3, width=40)
    Text1.grid(row=row, column=6)
    Label4 = tk.Label(frame1, text="附件:", bg="white")
    Label4.grid(row=row, column=7, ipady=12)
    Entry4 = tk.Entry(frame1, relief="solid")
    Entry4.grid(row=row, column=8, ipady=12)
    Button4 = tk.Button(frame1, text="选择文件", command=lambda r=row:choose_attachment(r))
    Button4.grid(row=row, column=9, ipady=8, ipadx=20, padx=5)
    del_button = tk.Button(frame1, text="删除")
    del_button.config(command=lambda r=row: del_rows(r))
    del_button.grid(row=row, column=10, ipady=8, ipadx=20, padx=5)
    Label5 = tk.Label(frame1, text="发送状态", bg="white")
    Label5.grid(row=row, column=11, ipady=12, padx=5)
    rows.append([Label0,Label1, Entry1, Label2, Entry2, Label3, Text1, Label4, Entry4, Button4,del_button, Label5])
    Entry1.insert(tk.END, recipient)
    Entry2.insert(tk.END, subject)
    Text1.insert(tk.END, message_body)
    Entry4.insert(tk.END, attachment)
    # 更新滚动区域
    CAN1.configure(scrollregion=CAN1.bbox("all"))
    check_scrollbar()



def del_rows(row):
    for row_data in rows[row]:
        row_data.grid_forget()  # 从布局中移除控件
    rows.pop(row)
    update_grid()

def update_grid():
        for i in range(0,len(rows)):
            for j in range(0,12):
                rows[i][j].grid(row=i,column=j)
                rows[i][10].config(command=lambda r=i: del_rows(r))
                rows[i][0].config(text = i+1 )

def choose_attachment(r):
    file_path = filedialog.askopenfilename(filetypes=[("All Files", "*.*")])
    if file_path:
        print(file_path)
        rows[r][8].delete(0,tk.END)
        rows[r][8].insert(tk.END, file_path)

def send_mail():
    # 获取发件人账户和密码/授权码
    global rows
    global user_name,passwd

    if (user_name != "" and passwd != ""):

        for row_data in rows:
            recipient = row_data[2].get()
            subject = row_data[4].get()
            message_body = row_data[6].get("1.0", tk.END)
            attachment = row_data[8].get()
            # 创建邮件对象
            message = MIMEMultipart()
            message["From"] = user_name
            message["To"] = recipient
            message["Subject"] = subject

            # 添加邮件正文
            message.attach(MIMEText(message_body, "plain"))


            # 添加附件
            if attachment:
                global subtype
                content_type, encoding = mimetypes.guess_type(attachment)
                print(content_type)
                if content_type is None or encoding is not None:
                    content_type = 'application/octet-stream'
                maintype, subtype = content_type.split('/', 1)
                with open(attachment, 'rb') as attachment_file:
                    attachment_part = MIMEBase(maintype, subtype)
                    attachment_part.set_payload(attachment_file.read())
                    attachment_part.add_header('Content-Disposition', 'attachment', filename=os.path.basename(attachment))
                    encoders.encode_base64(attachment_part)
                    message.attach(attachment_part)
            try:
                # 连接到SMTP服务器
                smtp_server = smtplib.SMTP("smtp.qq.com", 587)
                smtp_server.starttls()
                # 登录邮箱账户
                smtp_server.login(user_name, passwd)
                # 发送邮件
                smtp_server.send_message(message)
                # 关闭连接
                smtp_server.quit()

                # 更新发送状态
                row_data[11].config(text="已发送", bg="green")

                output_text.insert(tk.END,  "邮件发送成功 | 收件人:"+str(recipient)+ "  |  主题:" +str(subject)+ "  |  附件:" +str(attachment)+"\n")
            except Exception as e:
                # 发送失败,更新发送状态
                row_data[11].config(text="发送失败", bg="red")
                output_text.insert(tk.END, f"邮件发送失败:{recipient}\n")
                print("邮件发送失败:", e)


def send_mail_thread():
    global user_name,passwd
    read_config_file()
    if (len(rows) > 0 and passwd != ""):
        # 创建发送邮件线程
        thread = threading.Thread(target=send_mail)
        thread.start()
    elif len(rows)<=0:
        messagebox.showinfo("提示", "请至少添加一条收件信息")
    else:
        user_info_window()


def import_file():
    file_path = filedialog.askopenfilename(filetypes=[("Excel Files", "*.xlsx")])
    if file_path:
        wb = openpyxl.load_workbook(file_path)
        ws = wb.active
        for row in ws.iter_rows(min_row=2, values_only=True):
            recipient = row[0]
            subject = row[1]
            message_body = row[2]
            attachment = row[3] if len(row) > 3 else ""
            add_rows(recipient, subject, message_body, attachment)
            # 更新滚动区域
            CAN1.configure(scrollregion=CAN1.bbox("all"))
        wb.close()




window = tk.Tk()
window.title("启智星资料群发工具-2023")
window.minsize(1220, 500)
window.bind("<Configure>", windows_configure)
window.resizable(False,False)
screenWidth = window.winfo_screenwidth() # 获取显示区域的宽度
screenHeight = window.winfo_screenheight()  # 获取显示区域的高度
left = (screenWidth-1250) / 2
top = (screenHeight-500) / 2
window.geometry("1220x400+%d+%d" % (left,top))

tmp = open("tmp.ico","wb+")
tmp.write(base64.b64decode(b64str))
tmp.close()
window.iconbitmap("tmp.ico")
os.remove("tmp.ico")


# 创建主框架
RootFrame2 = tk.Frame(window, bg="white", width=1111)
RootFrame2.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
RootFrame3 = tk.Frame(window, bg="#e5e5e5", width=200, height=150)
RootFrame3.pack(side=tk.TOP, fill=tk.BOTH,expand=True)
RootFrame4 = tk.Frame(window, bg="#e5e5e5", width=200, height=200)
RootFrame4.pack(side=tk.TOP, fill=tk.X, expand=False)
# 创建滚动区域的容器
canvas_container = tk.Frame(RootFrame2, bg="white")
canvas_container.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, ipady=0)

# 创建滚动条
VSC = tk.Scrollbar(RootFrame2, orient="vertical")
VSC.pack(side=tk.RIGHT, fill=tk.Y)

# 创建Canvas并关联滚动条
CAN1 = tk.Canvas(canvas_container, bg="white", bd=1, yscrollcommand=VSC.set)
CAN1.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
VSC.configure(command=CAN1.yview)

# 创建内部框架以容纳内容
frame1 = tk.Frame(CAN1, bg="white", width=180, height=500)
CAN1.create_window((0, 0), window=frame1,anchor=tk.NW)
frame1.bind("<Configure>", on_configure)
Button1 = tk.Button(RootFrame3, text="添加一行", command=add_rows)
Button1.pack(side=tk.LEFT, anchor=tk.NW, ipadx=10, padx=10, pady=10)
Button2 = tk.Button(RootFrame3, text="导出日志", command=log_exp)
Button2.pack(side=tk.LEFT, anchor=tk.NW, ipadx=10, padx=10, pady=10)
Button3 = tk.Button(RootFrame3, text="导入文件", command=import_file)
Button3.pack(side=tk.LEFT, anchor=tk.NW, ipadx=10, padx=10, pady=10)
Button4 = tk.Button(RootFrame3, text="发送邮件", command=send_mail_thread)
Button4.pack(side=tk.LEFT, anchor=tk.NW, ipadx=10, padx=10, pady=10)
output_text = tk.Text(RootFrame4)
output_text.pack(side=tk.BOTTOM, fill=tk.BOTH,anchor=tk.NW, padx=0, pady=0)
# 菜单项
menu_bar = tk.Menu(window)
option_menu = tk.Menu(menu_bar, tearoff=0)
option_menu.add_command(label="设置发件账户", command=user_info_window)
option_menu.add_command(label="关于", command=msg_1)
# 将选项菜单添加到菜单栏
menu_bar.add_cascade(label="选项", menu=option_menu)
# 将菜单栏配置到主窗口
window.config(menu=menu_bar)
check_scrollbar()
window.mainloop()

GUI界面

liocejxx.png

成品下载

0

评论 (0)

取消