python群发邮件
需求说明
- 通过界面给不同人发送不同的主题、正文、附件的邮件
- 支持多线程
- 支持日志导出
支持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()
评论 (0)