diff --git a/.env.example b/.env.example index b073c91..aab5d0e 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,5 @@ TELEGRAM_TOKEN="" EXCHANGE_API_KEY="" UPDATE_OWNER_ID="" -AUTO_UPDATE_REMOTE="gitllc" +AUTO_UPDATE_REMOTE="https://git.llc/zimk/SubMind.git" AUTO_UPDATE_BRANCH="main" diff --git a/README.md b/README.md index 197dde6..f527fff 100644 --- a/README.md +++ b/README.md @@ -56,13 +56,16 @@ cp .env.example .env ```env TELEGRAM_TOKEN="" EXCHANGE_API_KEY="" +UPDATE_OWNER_ID="5355641202" +AUTO_UPDATE_REMOTE="https://git.llc/zimk/SubMind.git" +AUTO_UPDATE_BRANCH="main" ``` 说明: - `TELEGRAM_TOKEN` 必填。 - `EXCHANGE_API_KEY` 可选(不填时不做在线汇率转换)。 - `UPDATE_OWNER_ID` 可选(建议配置为你的 Telegram 用户 ID,仅该用户可执行 `/update`)。 -- `AUTO_UPDATE_REMOTE` 可选(默认 `gitllc`)。 +- `AUTO_UPDATE_REMOTE` 可选(默认 `https://git.llc/zimk/SubMind.git`)。 - `AUTO_UPDATE_BRANCH` 可选(默认 `main`)。 ### 4) 运行 diff --git a/SubMind.py b/SubMind.py index 62940a7..664bacd 100644 --- a/SubMind.py +++ b/SubMind.py @@ -46,8 +46,8 @@ DB_FILE = 'submind.db' # 自动更新配置 UPDATE_OWNER_ID = os.getenv('UPDATE_OWNER_ID') # 仅允许此用户执行 /update -AUTO_UPDATE_REMOTE = os.getenv('AUTO_UPDATE_REMOTE', '').strip() -AUTO_UPDATE_BRANCH = os.getenv('AUTO_UPDATE_BRANCH', 'main') +AUTO_UPDATE_REMOTE = os.getenv('AUTO_UPDATE_REMOTE', 'https://git.llc/zimk/SubMind.git').strip() +AUTO_UPDATE_BRANCH = os.getenv('AUTO_UPDATE_BRANCH', 'main').strip() or 'main' # --- 对话处理器状态 --- (ADD_NAME, ADD_COST, ADD_CURRENCY, ADD_CATEGORY, ADD_NEXT_DUE, @@ -848,7 +848,7 @@ async def add_next_due_received(update: Update, context: CallbackContext): parsed_date = parse_date(update.message.text) if not parsed_date: - await update.message.reply_text("无法识别的日期格式,请使用类似 '2025\\-10\\-01' 或 '10月1日' 的格式。") + await update.message.reply_text("无法识别的日期格式,请使用类似 '2025-10-01' 或 '10月1日' 的格式。") return ADD_NEXT_DUE sub_data['next_due'] = parsed_date keyboard = [ @@ -1022,7 +1022,7 @@ async def show_subscription_view(update: Update, context: CallbackContext, sub_i sub['reminders_enabled'], sub['notes']) freq_text = format_frequency(sub['frequency_unit'], sub['frequency_value']) main_currency = get_user_main_currency(user_id) - converted_cost = convert_currency(cost, currency, main_currency) + converted_cost = await asyncio.to_thread(convert_currency, cost, currency, main_currency) safe_name, safe_category, safe_freq = escape_html(name), escape_html(category), escape_html(freq_text) cost_str, converted_cost_str = escape_html(f"{cost:.2f}"), escape_html(f"{converted_cost:.2f}") renewal_text = "手动续费" if renewal_type == 'manual' else "自动续费" @@ -1400,7 +1400,7 @@ async def edit_new_value_received(update: Update, context: CallbackContext): parsed = parse_date(str(new_value)) if not parsed: if message_to_reply: - await message_to_reply.reply_text("无法识别的日期格式,请使用类似 '2025\\-10\\-01' 或 '10月1日' 的格式。") + await message_to_reply.reply_text("无法识别的日期格式,请使用类似 '2025-10-01' 或 '10月1日' 的格式。") validation_failed = True else: new_value = parsed @@ -1666,6 +1666,10 @@ def _resolve_update_target(repo_dir: str): return remotes[0], branch +def _run_cmd(cmd, cwd): + return subprocess.run(cmd, cwd=cwd, capture_output=True, text=True) + + async def update_bot(update: Update, context: CallbackContext): user_id = update.effective_user.id if not _can_run_update(user_id): @@ -1683,43 +1687,40 @@ async def update_bot(update: Update, context: CallbackContext): return fetch_cmd = ["git", "fetch", remote_name, branch_name] - fetch_proc = subprocess.run(fetch_cmd, cwd=repo_dir, capture_output=True, text=True) + fetch_proc = await asyncio.to_thread(_run_cmd, fetch_cmd, repo_dir) if fetch_proc.returncode != 0: err = (fetch_proc.stderr or fetch_proc.stdout or "未知错误").strip() await update.message.reply_text(f"更新失败(fetch):\n{escape_html(err)}", parse_mode='HTML') return - local_rev = subprocess.run( - ["git", "rev-parse", "HEAD"], cwd=repo_dir, capture_output=True, text=True - ) - remote_rev = subprocess.run( - ["git", "rev-parse", f"{remote_name}/{branch_name}"], - cwd=repo_dir, capture_output=True, text=True - ) + local_rev = await asyncio.to_thread(_run_cmd, ["git", "rev-parse", "HEAD"], repo_dir) + fetched_rev = await asyncio.to_thread(_run_cmd, ["git", "rev-parse", "FETCH_HEAD"], repo_dir) - if local_rev.returncode != 0 or remote_rev.returncode != 0: + if local_rev.returncode != 0 or fetched_rev.returncode != 0: await update.message.reply_text("更新失败:无法读取当前版本。") return local_hash = local_rev.stdout.strip() - remote_hash = remote_rev.stdout.strip() + fetched_hash = fetched_rev.stdout.strip() - if local_hash == remote_hash: + if local_hash == fetched_hash: await update.message.reply_text("当前已是最新版本,无需更新。") return - reset_proc = subprocess.run( - ["git", "reset", "--hard", f"{remote_name}/{branch_name}"], - cwd=repo_dir, capture_output=True, text=True + reset_proc = await asyncio.to_thread( + _run_cmd, + ["git", "reset", "--hard", "FETCH_HEAD"], + repo_dir ) if reset_proc.returncode != 0: err = (reset_proc.stderr or reset_proc.stdout or "未知错误").strip() await update.message.reply_text(f"更新失败(reset):\n{escape_html(err)}", parse_mode='HTML') return - pip_proc = subprocess.run( + pip_proc = await asyncio.to_thread( + _run_cmd, [sys.executable, "-m", "pip", "install", "-r", "requirements.txt"], - cwd=repo_dir, capture_output=True, text=True + repo_dir ) if pip_proc.returncode != 0: err = (pip_proc.stderr or pip_proc.stdout or "未知错误").strip() @@ -1727,7 +1728,7 @@ async def update_bot(update: Update, context: CallbackContext): return await update.message.reply_text( - f"更新完成({escape_html(remote_name)}/{escape_html(branch_name)}),正在重启机器人…", + f"更新完成({escape_html(remote_name)} {escape_html(branch_name)}),正在重启机器人…", parse_mode='HTML' ) os.execv(sys.executable, [sys.executable] + sys.argv)