python实现微信定时自动群聊发送消息
这篇教程将教你如何使用 Python 的 tkinter 库构建一个简单的定时任务发送工具,允许用户上传 Excel 文件,选择发送周期、发送时间,以及设置提醒类型(逾期提醒和快到期提醒)。此外,工具会在指定的时间根据 Excel 数据自动发送微信消息。
一、项目概述
该项目的核心功能是通过 Python 创建一个图形用户界面 (GUI),用户可以在其中设置定时任务,基于 Excel 文件中的任务信息,按照设置的时间周期自动发送消息。这些消息会包含任务的相关信息,并且提醒用户任务是否即将到期或已逾期。
二、项目需求
Python 3.x
安装以下依赖包:
tkinter:用于创建 GUI 界面。
schedule:用于定时任务的调度。
wxauto:用于发送微信消息。
pandas:用于读取和处理 Excel 数据。
datetime:用于处理日期时间。
安装依赖包:
pip install schedule wxauto pandas
三、代码结构解析
-
GUI 设计部分
使用 tkinter 来设计图形界面,界面包含文件上传、发送周期选择、任务发送时间设置、群聊名称输入以及选择提醒类型的复选框。
用户可以通过上传 Excel 文件、选择时间周期(每天或每周)、输入任务发送时间和群聊名称,来设置自动发送的任务。 -
主要函数解释
2.1 read_data(file_path):读取 Excel 文件
此函数使用 pandas 库读取 Excel 文件,并且填充空缺的字段(通过前向填充)。读取的内容包括工作任务、类别、责任人等信息。
def read_data(file_path):
file_path = file_path.lstrip()
df = pd.read_excel(file_path, usecols="A:J", nrows=75, skiprows=1)
df['工作任务'] = df['工作任务'].fillna(method='ffill')
df['类别'] = df['类别'].fillna(method='ffill')
df['责任人'] = df['责任人'].fillna(method='ffill')
return df
2.2 convert_to_month(plan_date):转换计划日期为月份差
此函数将 Excel 文件中的计划完成时间转换为当前月份的差值,判断任务是否逾期或接近到期
def convert_to_month(plan_date):
current_date = datetime.now()
current_month = current_date.month
current_year = current_date.year
plan_date = str(plan_date)
if '年' in plan_date:
year_month = plan_date.replace('年', '-').replace('月', '')
plan_date_obj = datetime.strptime(year_month, "%Y-%m")
diff = (current_year - plan_date_obj.year) * 12 + current_month - plan_date_obj.month
return diff
elif '-' in plan_date:
month = plan_date.split('-')[1].replace('月', '')
diff = current_month - int(month)
else:
diff = current_month - int(plan_date.replace('月', ''))
return diff
2.3 filter_rows(row):过滤任务
此函数用于根据任务的备注、进展和责任人等字段过滤数据,排除掉已完成、取消或没有设置完成时间的任务。
def filter_rows(row):
if '已完成' in str(row['备注']):
return False
if '已完成' in str(row['进展及存在问题']):
return False
if '取消' in str(row['责任人']):
return False
if '按规定' in str(row['计划完成时间']):
return False
return True
2.4 send_message(message):发送微信消息
此函数通过 wxauto 库发送微信消息。消息内容由任务的相关信息和提醒类型组成
def send_message(message):
wx = WeChat()
who = who_entry.get() # 获取用户输入的群聊名称
wx.SendMsg(message, who)
2.5 schedule_task(send_overdue, send_due_soon, file_path):定时任务处理
此函数是定时任务的核心部分,读取 Excel 数据,过滤符合条件的任务,生成任务消息,并根据设置的提醒类型(逾期提醒和快到期提醒)发送消息。
def schedule_task(send_overdue, send_due_soon, file_path):
df = read_data(file_path)
filtered_df = df[df.apply(filter_rows, axis=1)].reset_index(drop=True)
for i in range(len(filtered_df)):
overdue_months = convert_to_month(filtered_df.at[i, '计划完成时间'])
out_txt = f"@{filtered_df.at[i, '责任人']}\n"
out_txt += f"工作任务:{filtered_df.at[i, '工作任务']}\n"
out_txt += f"任务分解:{filtered_df.at[i, '任务分解']}\n"
out_txt += f"计划完成时间:{filtered_df.at[i, '计划完成时间']}\n"
if send_due_soon and overdue_months <= 0 and overdue_months >= -1:
out_txt += "\n=== 【快到期提醒】 ===\n"
out_txt += "该任务距离计划完成时间不足 1 个月,请尽快确认完成进度!\n"
send_message(out_txt)
if send_overdue and overdue_months > 0:
out_txt += "\n=== 【逾期警告】 ===\n"
out_txt += f"该任务已逾期 {overdue_months} 个月,请尽快处理!\n"
send_message(out_txt)
2.6 set_schedule(file_path):设置定时任务
根据用户选择的周期(每天或每周)、发送时间、提醒类型等设置定时任务。使用 schedule 库来执行定时任务。
def set_schedule(file_path):
time_str = time_entry.get()
send_overdue = overdue_check.get()
send_due_soon = due_soon_check.get()
try:
if time_period.get() == '每天':
# 设置每天发送任务
schedule.every().day.at(time_str).do(schedule_task, send_overdue=send_overdue, send_due_soon=send_due_soon,
file_path=file_path)
elif time_period.get() == '每周' and weekday_var.get():
# 设置每周发送任务,确保使用英语星期几(如 "Monday")
selected_day = weekday_var.get()
schedule.every(1).weeks.at(time_str).do(schedule_task, send_overdue=send_overdue,
send_due_soon=send_due_soon, file_path=file_path).day(selected_day)
else:
messagebox.showerror("错误", "请确保选择了合适的选项。")
messagebox.showinfo("设置成功", f"定时任务已设置为:{time_period.get()} {time_str}")
except Exception as e:
messagebox.showerror("设置失败", f"设置任务时出错:{str(e)}")
2.7 run_schedule():运行定时任务
此函数启动一个无限循环,持续检查并执行定时任务
def run_schedule():
while True:
schedule.run_pending()
time.sleep(1)
3. 完整的 GUI 设置
创建界面并处理用户交互,上传文件、设置定时任务、输入群聊名称等
# 创建 GUI 界面
root = tk.Tk()
root.title("定时任务发送设置")
root.geometry("600x600") # 增大窗口大小
root.resizable(True, True) # 允许调整大小
# 设置上传文件功能
tk.Label(root, text="上传 Excel 文件", font=('Arial', 12)).pack(pady=20)
upload_button = tk.Button(root, text="选择文件", command=lambda: upload_file(), font=('Arial', 12), width=20)
upload_button.pack()
file_label = tk.Label(root, text="尚未选择文件", font=('Arial', 12))
file_label.pack(pady=10)
# 设置发送周期选择框
tk.Label(root, text="选择发送周期", font=('Arial', 12)).pack(pady=20)
time_period = tk.StringVar(value='每天')
daily_radiobutton = tk.Radiobutton(root, text="每天", variable=time_period, value="每天", font=('Arial', 12),
command=update_mode)
weekly_radiobutton = tk.Radiobutton(root, text="每周", variable=time_period, value="每周", font=('Arial', 12),
command=update_mode)
daily_radiobutton.pack()
weekly_radiobutton.pack()
# 选择星期几(每周模式)
weekday_label = tk.Label(root, text="选择星期几发送任务", font=('Arial', 12))
weekday_var = tk.StringVar()
weekday_options = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', '
4. 完整代码
import tkinter as tk
from tkinter import messagebox, filedialog
import schedule
import time
import threading
from wxauto import *
import pandas as pd
from datetime import datetime
# 读取 Excel 数据
def read_data(file_path):
file_path = file_path.lstrip()
df = pd.read_excel(file_path, usecols="A:J", nrows=75, skiprows=1)
df['工作任务'] = df['工作任务'].fillna(method='ffill')
df['类别'] = df['类别'].fillna(method='ffill')
df['责任人'] = df['责任人'].fillna(method='ffill')
return df
# 转换日期
def convert_to_month(plan_date):
current_date = datetime.now()
current_month = current_date.month
current_year = current_date.year
plan_date = str(plan_date)
if '年' in plan_date:
year_month = plan_date.replace('年', '-').replace('月', '')
plan_date_obj = datetime.strptime(year_month, "%Y-%m")
diff = (current_year - plan_date_obj.year) * 12 + current_month - plan_date_obj.month
return diff
elif '-' in plan_date:
month = plan_date.split('-')[1].replace('月', '')
diff = current_month - int(month)
else:
diff = current_month - int(plan_date.replace('月', ''))
return diff
# 过滤数据
def filter_rows(row):
if '已完成' in str(row['备注']):
return False
if '已完成' in str(row['进展及存在问题']):
return False
if '取消' in str(row['责任人']):
return False
if '按规定' in str(row['计划完成时间']):
return False
return True
# 发送微信消息
def send_message(message):
wx = WeChat()
who = who_entry.get() # 获取用户输入的群聊名称
wx.SendMsg(message, who)
# 定义定时任务
def schedule_task(send_overdue, send_due_soon, file_path):
df = read_data(file_path)
filtered_df = df[df.apply(filter_rows, axis=1)].reset_index(drop=True)
for i in range(len(filtered_df)):
overdue_months = convert_to_month(filtered_df.at[i, '计划完成时间'])
out_txt = f"@{filtered_df.at[i, '责任人']}\n"
out_txt += f"工作任务:{filtered_df.at[i, '工作任务']}\n"
out_txt += f"任务分解:{filtered_df.at[i, '任务分解']}\n"
out_txt += f"计划完成时间:{filtered_df.at[i, '计划完成时间']}\n"
if send_due_soon and overdue_months <= 0 and overdue_months >= -1:
out_txt += "\n=== 【快到期提醒】 ===\n"
out_txt += "该任务距离计划完成时间不足 1 个月,请尽快确认完成进度!\n"
send_message(out_txt)
if send_overdue and overdue_months > 0:
out_txt += "\n=== 【逾期警告】 ===\n"
out_txt += f"该任务已逾期 {overdue_months} 个月,请尽快处理!\n"
send_message(out_txt)
# 设置定时任务
def set_schedule(file_path):
time_str = time_entry.get()
send_overdue = overdue_check.get()
send_due_soon = due_soon_check.get()
try:
if time_period.get() == '每天':
# 设置每天发送任务
schedule.every().day.at(time_str).do(schedule_task, send_overdue=send_overdue, send_due_soon=send_due_soon,
file_path=file_path)
elif time_period.get() == '每周' and weekday_var.get():
# 设置每周发送任务,确保使用英语星期几(如 "Monday")
selected_day = weekday_var.get()
schedule.every(1).weeks.at(time_str).do(schedule_task, send_overdue=send_overdue,
send_due_soon=send_due_soon, file_path=file_path).day(selected_day)
else:
messagebox.showerror("错误", "请确保选择了合适的选项。")
messagebox.showinfo("设置成功", f"定时任务已设置为:{time_period.get()} {time_str}")
except Exception as e:
messagebox.showerror("设置失败", f"设置任务时出错:{str(e)}")
# 运行任务
def run_schedule():
while True:
schedule.run_pending()
time.sleep(1)
# 更新 GUI,控制"选择星期几"下拉框的显示/隐藏
def update_mode():
if time_period.get() == '每天':
weekday_label.pack_forget()
weekday_dropdown.pack_forget()
elif time_period.get() == '每周':
weekday_label.pack(pady=10)
weekday_dropdown.pack()
# 文件上传功能
def upload_file():
file_path = filedialog.askopenfilename(filetypes=[("Excel Files", "*.xlsx")])
if file_path:
file_label.config(text="已选择文件: " + file_path)
return file_path
else:
messagebox.showwarning("未选择文件", "请上传一个有效的 Excel 文件。")
return None
# 创建 GUI 界面
root = tk.Tk()
root.title("定时任务发送设置")
root.geometry("600x600") # 增大窗口大小
root.resizable(True, True) # 允许调整大小
# 设置上传文件功能
tk.Label(root, text="上传 Excel 文件", font=('Arial', 12)).pack(pady=20)
upload_button = tk.Button(root, text="选择文件", command=lambda: upload_file(), font=('Arial', 12), width=20)
upload_button.pack()
file_label = tk.Label(root, text="尚未选择文件", font=('Arial', 12))
file_label.pack(pady=10)
# 设置发送周期选择框
tk.Label(root, text="选择发送周期", font=('Arial', 12)).pack(pady=20)
time_period = tk.StringVar(value='每天')
daily_radiobutton = tk.Radiobutton(root, text="每天", variable=time_period, value="每天", font=('Arial', 12),
command=update_mode)
weekly_radiobutton = tk.Radiobutton(root, text="每周", variable=time_period, value="每周", font=('Arial', 12),
command=update_mode)
daily_radiobutton.pack()
weekly_radiobutton.pack()
# 选择星期几(每周模式)
weekday_label = tk.Label(root, text="选择星期几发送任务", font=('Arial', 12))
weekday_var = tk.StringVar()
weekday_options = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
weekday_dropdown = tk.OptionMenu(root, weekday_var, *weekday_options)
weekday_dropdown.config(font=('Arial', 12))
# 设置发送时间输入框
tk.Label(root, text="请输入发送时间 (HH:MM)", font=('Arial', 12)).pack(pady=10)
time_entry = tk.Entry(root, font=('Arial', 12))
time_entry.pack(pady=5)
# 添加输入群聊名称的标签和输入框
tk.Label(root, text="输入群聊名称", font=('Arial', 12)).pack(pady=10)
who_entry = tk.Entry(root, font=('Arial', 12))
who_entry.pack(pady=5)
# 设置发送提醒类型复选框
overdue_check = tk.BooleanVar(value=True)
due_soon_check = tk.BooleanVar(value=True)
tk.Checkbutton(root, text="发送逾期提醒", variable=overdue_check, font=('Arial', 12)).pack(pady=5)
tk.Checkbutton(root, text="发送快到期提醒", variable=due_soon_check, font=('Arial', 12)).pack(pady=5)
# 设置开始按钮
tk.Button(root, text="设置定时任务", command=lambda: set_schedule(
file_label.cget("text")[6:] if file_label.cget("text").startswith("已选择文件") else None), font=('Arial', 14), width=20,
height=2).pack(pady=20)
# 启动定时任务线程
threading.Thread(target=run_schedule, daemon=True).start()
# 启动 GUI
root.mainloop()