log.error("邮件发送失败: %s", e)
+ def send_resend_link_email():
+ """发送重发链接邮件(不含二维码,仅文字+链接)"""
+ if not EMAIL_ENABLED or not EMAIL_ACCOUNT:
+ log.warning("邮件未启用或账号未配置,跳过重发邮件")
+ return
+ try:
+ from email.mime.text import MIMEText
+ msg = MIMEText(
+ "微信登录二维码提醒已全部发送完毕,如仍需登录请点击以下链接申请重新发送:\n\n"
+ "https://szjxzxh.cn/wechat-resend.html\n\n"
+ "(点击后监控程序将重新开始发送二维码通知)",
+ "plain", "utf-8"
+ )
+ from email.utils import formataddr
+ msg["From"] = formataddr(("微信助手", EMAIL_ACCOUNT))
+ msg["To"] = EMAIL_TO
+ msg["Subject"] = "微信登录二维码已过期 - 申请重发"
+
+ if USE_SSL:
+ s = smtplib.SMTP_SSL(SMTP_SERVER, SMTP_PORT, timeout=10)
+ else:
+ s = smtplib.SMTP(SMTP_SERVER, SMTP_PORT, timeout=10)
+ s.starttls()
+ s.login(EMAIL_ACCOUNT, EMAIL_PASSWORD)
+ s.send_message(msg)
+ s.quit()
+ log.info("重发链接邮件已发送至 %s", EMAIL_TO)
+ except Exception as e:
+ log.error("重发链接邮件发送失败: %s", e)
+
+
def _find_wechat_windows():
"""找到标题严格等于'微信'的可见窗口列表(排除包含'微信'的其他窗口)"""
return [w for w in gw.getAllWindows() if w.title == "微信"]
self.qr_sent_count = 0
self._last_login_hash = None
self._user_resized = False
+ self._resend_link_sent = False
self._setup_ui()
self.last_md5 = None
self.qr_sent_count = 0
self._last_login_hash = None
+ self._resend_link_sent = False
log.info("监控已停用")
# ── status ──
self._set_status("微信未运行,启动中...")
return # _do_detect 会在下一轮自动启动微信
- # ② 检查邮件是否已发满(含重发次数)
+ # ② 检查邮件已发满后的处理
if self.hash_mode_emails_sent >= MAX_EMAILS:
- # 已发满,进入等待重发模式
- self._check_resend_flag()
- return
+ if not self._resend_link_sent:
+ # 还没发第6封链接邮件,继续监测窗口
+ pass # 不走下面,会继续执行到窗口尺寸检查
+ else:
+ # 第6封已发,进入等待重发模式
+ self._check_resend_flag()
+ return
# ③ 查窗口尺寸
wechat_windows = _find_wechat_windows()
# 仍然是登录窗口→发通知
time.sleep(2)
img2 = _capture_center()
- qr_path = os.path.join(tempfile.gettempdir(), f"wechat_qr_{int(time.time())}.png")
- img2.save(qr_path, "PNG")
- self.hash_mode_emails_sent += 1
+ if self.hash_mode_emails_sent >= MAX_EMAILS:
+ if not self._resend_link_sent:
+ # 第5封已发完,现在二维码过期了,发第6封链接邮件
+ send_resend_link_email()
+ self._resend_link_sent = True
+ self._set_status("二维码已过期,等待重发")
+ log.info("二维码已过期,第6封链接邮件已发送")
+ self.root.after(PAUSE * 1000, self._check_resend_flag)
+ return
+ else:
+ # 正常发邮件(第1-5封)
+ qr_path = os.path.join(tempfile.gettempdir(), f"wechat_qr_{int(time.time())}.png")
+ img2.save(qr_path, "PNG")
+
+ self.hash_mode_emails_sent += 1
- # 发邮件
- send_qr_email(qr_path)
+ # 发邮件
+ send_qr_email(qr_path)
- # 短信:只在第1次发(无论多少轮,只发一次)
- if self.hash_mode_emails_sent == 1:
- if SMS_PHONE:
- send_sms("888888", SMS_PHONE)
+ # 短信:只在第1次发(无论多少轮,只发一次)
+ if self.hash_mode_emails_sent == 1:
+ if SMS_PHONE:
+ send_sms("888888", SMS_PHONE)
- # 第5封邮件末尾带重发链接
- if self.hash_mode_emails_sent == MAX_EMAILS:
- # TODO: 发第5封邮件时包含重发链接
- log.info("邮件已发满 %d 次,进入等待重发模式", MAX_EMAILS)
+ # 第5封邮件已发完,继续监测,等二维码过期再发第6封
+ if self.hash_mode_emails_sent >= MAX_EMAILS and not self._resend_link_sent:
+ log.info("第5封邮件已发送,等待二维码过期...")
+ self._set_status("第5封已发送,等待二维码过期")
- self.last_md5 = _md5_of_image(img2)
- self._set_status(f"请扫码登录 ({self.hash_mode_emails_sent}/{MAX_EMAILS})")
- self.root.after(PAUSE * 1000, self._hash_loop_tick)
+ self.last_md5 = _md5_of_image(img2)
+ self._set_status(f"请扫码登录 ({self.hash_mode_emails_sent}/{MAX_EMAILS})")
+ self.root.after(PAUSE * 1000, self._hash_loop_tick)
def _check_resend_flag(self):
"""检查服务器上是否有重发标记"""
- url = "https://szjxzxh.cn/wechat_resend.flag"
+
+ # ① 先查窗口尺寸
+ wechat_windows = _find_wechat_windows()
+ visible = [w for w in wechat_windows if w.visible]
+ login_window = None
+ for w in visible:
+ if w.width < 700 and w.height < 850:
+ login_window = w
+ break
+
+ if not login_window:
+ # 窗口变大了 → 已登录
+ self.hash_mode = False
+ self._last_login_hash = None
+ self.qr_sent_count = 0
+ self._resend_link_sent = False
+ self._set_status("微信运行中")
+ log.info("微信已登录,退出等待重发模式")
+ return
+
+ # ② 查flag
import requests
+ url = "https://szjxzxh.cn/wechat_resend.flag"
try:
resp = requests.get(url, timeout=5)
if resp.status_code == 200:
# 用户申请了重发,删除flag文件
- # 先删服务器上的flag
delete_url = "https://szjxzxh.cn/cgi/wechat_resend.py?action=delete"
try:
requests.get(delete_url, timeout=5)
# 重置计数,开始新一轮
self.hash_mode_emails_sent = 0
self.last_md5 = None
+ self._resend_link_sent = False
self._set_status("准备重新发送通知...")
log.info("检测到重发请求,开始新一轮通知")
self.root.after(PAUSE * 1000, self._hash_loop_tick)