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