perf(update): pin default remote URL and offload blocking update/currency ops
This commit is contained in:
@@ -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"
|
||||||
|
|||||||
@@ -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) 运行
|
||||||
|
|||||||
45
SubMind.py
45
SubMind.py
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user