From fcdafcd4b2790d4d8247d1d43d733ec158ae43b7 Mon Sep 17 00:00:00 2001 From: Xiaolan Bot Date: Mon, 24 Aug 2026 20:35:48 +0800 Subject: [PATCH] fix: handle PEP 668 env in /update; harden reminders and CSV import; add ptb job-queue extra --- SubMind.py | 30 ++++++++++++++++++------------ requirements.txt | 2 +- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/SubMind.py b/SubMind.py index dfb2989..7351173 100644 --- a/SubMind.py +++ b/SubMind.py @@ -418,8 +418,8 @@ async def check_and_send_reminders(context: CallbackContext): message += " 将会自动续费。" keyboard = None - elif renewal_type == 'manual' and sub['reminder_days'] > 0: - reminder_date = due_date - datetime.timedelta(days=sub['reminder_days']) + elif renewal_type == 'manual' and int(sub['reminder_days'] or 0) > 0: + reminder_date = due_date - datetime.timedelta(days=int(sub['reminder_days'])) if reminder_date == today: days_left = (due_date - today).days days_text = f"{days_left}天后" if days_left > 0 else "今天" @@ -440,7 +440,7 @@ async def check_and_send_reminders(context: CallbackContext): logger.info(f"Reminder sent for sub_id {sub['id']}") except Exception as e: - logger.error(f"Failed to process reminder for sub_id {sub.get('id', 'N/A')}: {e}") + logger.error(f"Failed to process reminder for sub_id {sub['id']}: {e}") # --- 命令处理器 --- async def start(update: Update, context: CallbackContext): user_id = update.effective_user.id @@ -746,7 +746,7 @@ async def import_upload_received(update: Update, context: CallbackContext): renewal_base = str(row['renewal_base']).lower().strip() if renewal_base not in valid_renewal_bases: raise ValueError(f"无效续订起算方式: {renewal_base}") - notes = str(row['notes']).strip() if pd.notna(row['notes']) else None + notes = str(row['notes']).strip() if ('notes' in df.columns and pd.notna(row['notes'])) else None if notes and len(notes) > MAX_NOTES_LEN: raise ValueError(f"备注过长(>{MAX_NOTES_LEN})") name = str(row['name']).strip() @@ -1804,15 +1804,18 @@ async def update_bot(update: Update, context: CallbackContext): await update.message.reply_text(f"更新失败(reset):\n{escape_html(err)}", parse_mode='HTML') return - pip_proc = await asyncio.to_thread( - _run_cmd, - [sys.executable, "-m", "pip", "install", "-r", "requirements.txt"], - repo_dir - ) + pip_cmd = [sys.executable, "-m", "pip", "install", "-r", "requirements.txt"] + pip_proc = await asyncio.to_thread(_run_cmd, pip_cmd, repo_dir) if pip_proc.returncode != 0: - err = (pip_proc.stderr or pip_proc.stdout or "未知错误").strip() - await update.message.reply_text(f"依赖安装失败:\n{escape_html(err[-1800:])}", parse_mode='HTML') - return + pip_output = (pip_proc.stderr or "") + (pip_proc.stdout or "") + # Debian/Ubuntu 的系统托管 Python(PEP 668)会拒绝直接安装,需加 --break-system-packages + if "externally-managed-environment" in pip_output: + await update.message.reply_text("检测到系统托管的 Python 环境(PEP 668),改用 --break-system-packages 重试…") + pip_proc = await asyncio.to_thread(_run_cmd, pip_cmd + ["--break-system-packages"], repo_dir) + if pip_proc.returncode != 0: + err = (pip_proc.stderr or pip_proc.stdout or "未知错误").strip() + await update.message.reply_text(f"依赖安装失败:\n{escape_html(err[-1800:])}", parse_mode='HTML') + return await update.message.reply_text( f"更新完成({escape_html(remote_name)} {escape_html(branch_name)}),正在重启机器人…", @@ -1870,6 +1873,9 @@ def main(): time=datetime.time(hour=9, minute=0, tzinfo=datetime.timezone(datetime.timedelta(hours=8))), name='daily_reminders' ) + # 启动后稍作等待补跑一次,避免重启导致当天的提醒被跳过(函数内部按 last_reminded_date 去重) + app.job_queue.run_once(check_and_send_reminders, when=datetime.timedelta(seconds=15), + name='startup_reminder_catchup') logger.info("Daily reminder job scheduled.") application.post_init = post_init diff --git a/requirements.txt b/requirements.txt index 47cfca3..ac00acf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -python-telegram-bot>=20.0 +python-telegram-bot[job-queue]>=20.0 pandas matplotlib python-dateutil