fix: handle PEP 668 env in /update; harden reminders and CSV import; add ptb job-queue extra

This commit is contained in:
Xiaolan Bot
2026-08-24 20:35:48 +08:00
parent 2c9d538f55
commit fcdafcd4b2
2 changed files with 19 additions and 13 deletions
+18 -12
View File
@@ -418,8 +418,8 @@ async def check_and_send_reminders(context: CallbackContext):
message += " 将会自动续费。" message += " 将会自动续费。"
keyboard = None keyboard = None
elif renewal_type == 'manual' and sub['reminder_days'] > 0: elif renewal_type == 'manual' and int(sub['reminder_days'] or 0) > 0:
reminder_date = due_date - datetime.timedelta(days=sub['reminder_days']) reminder_date = due_date - datetime.timedelta(days=int(sub['reminder_days']))
if reminder_date == today: if reminder_date == today:
days_left = (due_date - today).days days_left = (due_date - today).days
days_text = f"<b>{days_left}天后</b>" if days_left > 0 else "<b>今天</b>" days_text = f"<b>{days_left}天后</b>" if days_left > 0 else "<b>今天</b>"
@@ -440,7 +440,7 @@ async def check_and_send_reminders(context: CallbackContext):
logger.info(f"Reminder sent for sub_id {sub['id']}") logger.info(f"Reminder sent for sub_id {sub['id']}")
except Exception as e: 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): async def start(update: Update, context: CallbackContext):
user_id = update.effective_user.id 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() renewal_base = str(row['renewal_base']).lower().strip()
if renewal_base not in valid_renewal_bases: if renewal_base not in valid_renewal_bases:
raise ValueError(f"无效续订起算方式: {renewal_base}") 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: if notes and len(notes) > MAX_NOTES_LEN:
raise ValueError(f"备注过长(>{MAX_NOTES_LEN}") raise ValueError(f"备注过长(>{MAX_NOTES_LEN}")
name = str(row['name']).strip() 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<code>{escape_html(err)}</code>", parse_mode='HTML') await update.message.reply_text(f"更新失败(reset):\n<code>{escape_html(err)}</code>", parse_mode='HTML')
return return
pip_proc = await asyncio.to_thread( pip_cmd = [sys.executable, "-m", "pip", "install", "-r", "requirements.txt"]
_run_cmd, pip_proc = await asyncio.to_thread(_run_cmd, pip_cmd, repo_dir)
[sys.executable, "-m", "pip", "install", "-r", "requirements.txt"],
repo_dir
)
if pip_proc.returncode != 0: if pip_proc.returncode != 0:
err = (pip_proc.stderr or pip_proc.stdout or "未知错误").strip() pip_output = (pip_proc.stderr or "") + (pip_proc.stdout or "")
await update.message.reply_text(f"依赖安装失败:\n<code>{escape_html(err[-1800:])}</code>", parse_mode='HTML') # Debian/Ubuntu 的系统托管 PythonPEP 668)会拒绝直接安装,需加 --break-system-packages
return 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<code>{escape_html(err[-1800:])}</code>", parse_mode='HTML')
return
await update.message.reply_text( await update.message.reply_text(
f"更新完成({escape_html(remote_name)} {escape_html(branch_name)}),正在重启机器人…", 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))), time=datetime.time(hour=9, minute=0, tzinfo=datetime.timezone(datetime.timedelta(hours=8))),
name='daily_reminders' 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.") logger.info("Daily reminder job scheduled.")
application.post_init = post_init application.post_init = post_init
+1 -1
View File
@@ -1,4 +1,4 @@
python-telegram-bot>=20.0 python-telegram-bot[job-queue]>=20.0
pandas pandas
matplotlib matplotlib
python-dateutil python-dateutil