perf(update): pin default remote URL and offload blocking update/currency ops

This commit is contained in:
Xiaolan Bot
2026-02-25 16:22:53 +08:00
parent 0904acad4e
commit 205de16479
3 changed files with 28 additions and 24 deletions

View File

@@ -1,5 +1,5 @@
TELEGRAM_TOKEN="" TELEGRAM_TOKEN=""
EXCHANGE_API_KEY="" EXCHANGE_API_KEY=""
UPDATE_OWNER_ID="" UPDATE_OWNER_ID=""
AUTO_UPDATE_REMOTE="gitllc" AUTO_UPDATE_REMOTE="https://git.llc/zimk/SubMind.git"
AUTO_UPDATE_BRANCH="main" AUTO_UPDATE_BRANCH="main"

View File

@@ -56,13 +56,16 @@ cp .env.example .env
```env ```env
TELEGRAM_TOKEN="<YOUR_TELEGRAM_BOT_TOKEN>" TELEGRAM_TOKEN="<YOUR_TELEGRAM_BOT_TOKEN>"
EXCHANGE_API_KEY="<YOUR_EXCHANGE_API_KEY>" EXCHANGE_API_KEY="<YOUR_EXCHANGE_API_KEY>"
UPDATE_OWNER_ID="5355641202"
AUTO_UPDATE_REMOTE="https://git.llc/zimk/SubMind.git"
AUTO_UPDATE_BRANCH="main"
``` ```
说明: 说明:
- `TELEGRAM_TOKEN` 必填。 - `TELEGRAM_TOKEN` 必填。
- `EXCHANGE_API_KEY` 可选(不填时不做在线汇率转换)。 - `EXCHANGE_API_KEY` 可选(不填时不做在线汇率转换)。
- `UPDATE_OWNER_ID` 可选(建议配置为你的 Telegram 用户 ID仅该用户可执行 `/update`)。 - `UPDATE_OWNER_ID` 可选(建议配置为你的 Telegram 用户 ID仅该用户可执行 `/update`)。
- `AUTO_UPDATE_REMOTE` 可选(默认 `gitllc`)。 - `AUTO_UPDATE_REMOTE` 可选(默认 `https://git.llc/zimk/SubMind.git`)。
- `AUTO_UPDATE_BRANCH` 可选(默认 `main`)。 - `AUTO_UPDATE_BRANCH` 可选(默认 `main`)。
### 4) 运行 ### 4) 运行

View File

@@ -46,8 +46,8 @@ DB_FILE = 'submind.db'
# 自动更新配置 # 自动更新配置
UPDATE_OWNER_ID = os.getenv('UPDATE_OWNER_ID') # 仅允许此用户执行 /update UPDATE_OWNER_ID = os.getenv('UPDATE_OWNER_ID') # 仅允许此用户执行 /update
AUTO_UPDATE_REMOTE = os.getenv('AUTO_UPDATE_REMOTE', '').strip() AUTO_UPDATE_REMOTE = os.getenv('AUTO_UPDATE_REMOTE', 'https://git.llc/zimk/SubMind.git').strip()
AUTO_UPDATE_BRANCH = os.getenv('AUTO_UPDATE_BRANCH', 'main') AUTO_UPDATE_BRANCH = os.getenv('AUTO_UPDATE_BRANCH', 'main').strip() or 'main'
# --- 对话处理器状态 --- # --- 对话处理器状态 ---
(ADD_NAME, ADD_COST, ADD_CURRENCY, ADD_CATEGORY, ADD_NEXT_DUE, (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) parsed_date = parse_date(update.message.text)
if not parsed_date: 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 return ADD_NEXT_DUE
sub_data['next_due'] = parsed_date sub_data['next_due'] = parsed_date
keyboard = [ keyboard = [
@@ -1022,7 +1022,7 @@ async def show_subscription_view(update: Update, context: CallbackContext, sub_i
sub['reminders_enabled'], sub['notes']) sub['reminders_enabled'], sub['notes'])
freq_text = format_frequency(sub['frequency_unit'], sub['frequency_value']) freq_text = format_frequency(sub['frequency_unit'], sub['frequency_value'])
main_currency = get_user_main_currency(user_id) 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) 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}") cost_str, converted_cost_str = escape_html(f"{cost:.2f}"), escape_html(f"{converted_cost:.2f}")
renewal_text = "手动续费" if renewal_type == 'manual' else "自动续费" 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)) parsed = parse_date(str(new_value))
if not parsed: if not parsed:
if message_to_reply: 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 validation_failed = True
else: else:
new_value = parsed new_value = parsed
@@ -1666,6 +1666,10 @@ def _resolve_update_target(repo_dir: str):
return remotes[0], branch 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): async def update_bot(update: Update, context: CallbackContext):
user_id = update.effective_user.id user_id = update.effective_user.id
if not _can_run_update(user_id): if not _can_run_update(user_id):
@@ -1683,43 +1687,40 @@ async def update_bot(update: Update, context: CallbackContext):
return return
fetch_cmd = ["git", "fetch", remote_name, branch_name] 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: if fetch_proc.returncode != 0:
err = (fetch_proc.stderr or fetch_proc.stdout or "未知错误").strip() err = (fetch_proc.stderr or fetch_proc.stdout or "未知错误").strip()
await update.message.reply_text(f"更新失败fetch\n<code>{escape_html(err)}</code>", parse_mode='HTML') await update.message.reply_text(f"更新失败fetch\n<code>{escape_html(err)}</code>", parse_mode='HTML')
return return
local_rev = subprocess.run( local_rev = await asyncio.to_thread(_run_cmd, ["git", "rev-parse", "HEAD"], repo_dir)
["git", "rev-parse", "HEAD"], cwd=repo_dir, capture_output=True, text=True fetched_rev = await asyncio.to_thread(_run_cmd, ["git", "rev-parse", "FETCH_HEAD"], repo_dir)
)
remote_rev = subprocess.run(
["git", "rev-parse", f"{remote_name}/{branch_name}"],
cwd=repo_dir, capture_output=True, text=True
)
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("更新失败:无法读取当前版本。") await update.message.reply_text("更新失败:无法读取当前版本。")
return return
local_hash = local_rev.stdout.strip() 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("当前已是最新版本,无需更新。") await update.message.reply_text("当前已是最新版本,无需更新。")
return return
reset_proc = subprocess.run( reset_proc = await asyncio.to_thread(
["git", "reset", "--hard", f"{remote_name}/{branch_name}"], _run_cmd,
cwd=repo_dir, capture_output=True, text=True ["git", "reset", "--hard", "FETCH_HEAD"],
repo_dir
) )
if reset_proc.returncode != 0: if reset_proc.returncode != 0:
err = (reset_proc.stderr or reset_proc.stdout or "未知错误").strip() err = (reset_proc.stderr or reset_proc.stdout or "未知错误").strip()
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 = subprocess.run( pip_proc = await asyncio.to_thread(
_run_cmd,
[sys.executable, "-m", "pip", "install", "-r", "requirements.txt"], [sys.executable, "-m", "pip", "install", "-r", "requirements.txt"],
cwd=repo_dir, capture_output=True, text=True repo_dir
) )
if pip_proc.returncode != 0: if pip_proc.returncode != 0:
err = (pip_proc.stderr or pip_proc.stdout or "未知错误").strip() err = (pip_proc.stderr or pip_proc.stdout or "未知错误").strip()
@@ -1727,7 +1728,7 @@ async def update_bot(update: Update, context: CallbackContext):
return 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)}),正在重启机器人…",
parse_mode='HTML' parse_mode='HTML'
) )
os.execv(sys.executable, [sys.executable] + sys.argv) os.execv(sys.executable, [sys.executable] + sys.argv)