refactor: use psutil uptime and wmi lib for Windows temps; graceful SIGINT/SIGTERM/SIGBREAK shutdown

This commit is contained in:
snowy-yang committed 2026-09-12 20:18:26 +08:00
1 parent e39d1a0f52
commit 593b8011c6
4 files changed
+70 -20

No files matched your search

+30 -4
View File
@@ -2,6 +2,7 @@
import asyncio
import logging
import re
import signal
import sys
from pathlib import Path
@@ -260,15 +261,36 @@ class Bot:
watcher = asyncio.get_running_loop().create_task(self._watch_config(alerter))
log.info("机器人就绪,等待 OneBot 实现端连接...")
try:
await asyncio.Event().wait()
except (KeyboardInterrupt, asyncio.CancelledError):
pass
await self._wait_for_stop()
finally:
log.info("正在关闭...")
watcher.cancel()
await updater.stop()
await reporter.stop()
await alerter.stop()
await self.ob.stop()
log.info("已退出")
async def _wait_for_stop(self):
"""阻塞直到收到 Ctrl-C / SIGTERM。"""
stop = asyncio.Event()
loop = asyncio.get_running_loop()
def request_stop():
loop.call_soon_threadsafe(stop.set)
sigs = [signal.SIGINT, signal.SIGTERM]
if sys.platform == "win32" and hasattr(signal, "SIGBREAK"):
sigs.append(signal.SIGBREAK) # CTRL_BREAK / 任务管理器停止服务
for sig in sigs:
try:
loop.add_signal_handler(sig, request_stop)
except NotImplementedError: # Windows 回退
signal.signal(sig, lambda s, f: request_stop())
try:
await stop.wait()
except KeyboardInterrupt: # Windows 上 Ctrl-C 可能直接中断 await
pass
async def _watch_config(self, alerter: Alerter, interval: float = 5.0):
"""监测 config.toml 修改并热重载(端口/token 等需重启的项不生效)。"""
@@ -294,7 +316,11 @@ def main():
if sys.platform == "win32":
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
cfg = load_config()
asyncio.run(Bot(cfg).run())
try:
asyncio.run(Bot(cfg).run())
except KeyboardInterrupt:
print("已退出")
sys.exit(0)
if __name__ == "__main__":
+6 -16
View File
@@ -110,22 +110,16 @@ def get_memory() -> dict:
def _wmi_temps() -> dict:
"""Windows: 通过 WMI 热区读取温度(部分主板支持,可能需要管理员权限)。"""
import subprocess
try:
out = subprocess.run(
["powershell", "-NoProfile", "-Command",
"Get-CimInstance -Namespace root/wmi -ClassName MSAcpi_ThermalZoneTemperature "
"| Select-Object -ExpandProperty CurrentTemperature"],
capture_output=True, text=True, timeout=10,
encoding="utf-8", errors="replace",
)
values = [float(x) for x in out.stdout.split() if x.replace(".", "", 1).isdigit()]
except (OSError, subprocess.SubprocessError, ValueError):
import wmi # 仅 Windows 安装
zones = wmi.WMI(namespace="root\\wmi").MSAcpi_ThermalZoneTemperature()
values = [z.CurrentTemperature for z in zones]
except Exception: # 类不存在 / 权限不足 / 非 Windows
return {}
# WMI 单位是 0.1 开尔文
return {"ThermalZone": [
{"label": f"zone{i}", "current": round(v / 10 - 273.15, 1)}
for i, v in enumerate(values)
for i, v in enumerate(values) if v
]}
@@ -249,11 +243,7 @@ def get_top_memory(n: int = 10) -> list:
def get_uptime() -> float:
try:
with open("/proc/uptime") as f:
return float(f.read().split()[0])
except OSError:
return time.time() - psutil.boot_time()
return time.time() - psutil.boot_time()
def get_status(top_n: int | None = None) -> dict:
+1
View File
@@ -11,6 +11,7 @@ dependencies = [
"aiohttp>=3.14.3",
"gitpython>=3.1.62",
"psutil>=7.2.2",
"wmi>=1.5 ; sys_platform == 'win32'",
]
[dependency-groups]
Generated
+33
View File
@@ -281,6 +281,7 @@ dependencies = [
{ name = "aiohttp" },
{ name = "gitpython" },
{ name = "psutil" },
{ name = "wmi", marker = "sys_platform == 'win32'" },
]
[package.dev-dependencies]
@@ -294,6 +295,7 @@ requires-dist = [
{ name = "aiohttp", specifier = ">=3.14.3" },
{ name = "gitpython", specifier = ">=3.1.62" },
{ name = "psutil", specifier = ">=7.2.2" },
{ name = "wmi", marker = "sys_platform == 'win32'", specifier = ">=1.5" },
]
[package.metadata.requires-dev]
@@ -618,6 +620,25 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/03/e2/08a497ef684b88559c9cc5f4ad53a37e7b99e727094a86d6ea32536d5d3c/pytest_asyncio-1.4.0-py3-none-any.whl", hash = "sha256:933ca923a23075a87fb7070c0ec272a6848489824d887c85c812670932835aa1", size = 16930, upload-time = "2026-05-26T09:56:02.576Z" },
]
[[package]]
name = "pywin32"
version = "312"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/83/ff/32aa7d2ed0ab12b323aaa64f9b75e6ad4f8fd09f9ccfc28c79414d46838d/pywin32-312-cp312-cp312-win32.whl", hash = "sha256:dab4f65ac9c4e48400a2a0530c46c3c579cd5905ecd11b80692373915269208b", size = 6371877, upload-time = "2026-06-04T07:49:28.836Z" },
{ url = "https://files.pythonhosted.org/packages/03/d9/77040d3b43df3f3be32ea289433d660d2727f5ba327bc73be835127d9d60/pywin32-312-cp312-cp312-win_amd64.whl", hash = "sha256:b457f6d628a47e8a7346ce22acb7e1a46a4a78b52e1d17e1af56871bd19a93bc", size = 6914841, upload-time = "2026-06-04T07:49:31.85Z" },
{ url = "https://files.pythonhosted.org/packages/e3/cc/7b1ec671775756020a0ee7f4feeaf3c568f0ab86bd3900088cf986937a92/pywin32-312-cp312-cp312-win_arm64.whl", hash = "sha256:6017c58e12f6809fbb0555b75df144c2922a9ffd18e4b9b5afa863b6c1a9d950", size = 6727901, upload-time = "2026-06-04T07:49:34.244Z" },
{ url = "https://files.pythonhosted.org/packages/2d/41/12fbfd7f36ed2146d8bc9de96c2741296bf0d490b98508496cff322e274c/pywin32-312-cp313-cp313-win32.whl", hash = "sha256:7a27df850933d16a8eabfbaeb73d52b273e2da667f80d70b01a89d1f6828d02c", size = 6370184, upload-time = "2026-06-04T07:49:36.253Z" },
{ url = "https://files.pythonhosted.org/packages/ba/db/36a78e3403099d31d9746d13fdcde5accc43c1155f375a34d15983a479a7/pywin32-312-cp313-cp313-win_amd64.whl", hash = "sha256:c53e878d15a1c44788082bfe712a905433473aa38f86375b7cf8b45e3acbaaf9", size = 6914298, upload-time = "2026-06-04T07:49:38.876Z" },
{ url = "https://files.pythonhosted.org/packages/84/37/c1697194092b76de9ed47ca124323f02c57ffc8a45c06f88a3d5acaf01eb/pywin32-312-cp313-cp313-win_arm64.whl", hash = "sha256:59aba5d5940842075343a5ddc6b11f1cdf0d1567fe745290359dfbcc7c2eb831", size = 6727640, upload-time = "2026-06-04T07:49:41.083Z" },
{ url = "https://files.pythonhosted.org/packages/fc/2b/1f3cded5822fd49c02f40544cbb5f58c7cfd6b1694869fd476cb6170ee97/pywin32-312-cp314-cp314-win32.whl", hash = "sha256:a77a90fbb6881238d2ca9c6fd797b25817f3768fe78d214a90137ff055a75f5b", size = 6468928, upload-time = "2026-06-04T07:49:43.188Z" },
{ url = "https://files.pythonhosted.org/packages/21/82/3bf86d2e2808902013132e1ce905a7da0da53790f3836c64bf44d55e24f3/pywin32-312-cp314-cp314-win_amd64.whl", hash = "sha256:a4dd3a848290ef724347b19f301045831d8e802fa4464f491b98b1e0a081432e", size = 7024157, upload-time = "2026-06-04T07:49:45.34Z" },
{ url = "https://files.pythonhosted.org/packages/a4/0e/73f6d6800b4f27655abd9e9f6aaeaefcddb2b946e4674efa2bab184a7f7b/pywin32-312-cp314-cp314-win_arm64.whl", hash = "sha256:9fce94568364e0155e6dfb781ac5d95903be8baf28670632beab1b523f300daa", size = 6839598, upload-time = "2026-06-04T07:49:47.613Z" },
{ url = "https://files.pythonhosted.org/packages/eb/61/caa39686032d2ebdd04ff0ab5cbe163126c0066d98e00c9018646e42393b/pywin32-312-cp315-cp315-win32.whl", hash = "sha256:5c1fbe4a937a73ae9297384a3da38518cbc694c68ad8a809b2e19acd350f03ed", size = 6471159, upload-time = "2026-06-04T07:49:50.035Z" },
{ url = "https://files.pythonhosted.org/packages/0f/cd/7e1de64a4a6f69c04214169657ccab0d93a670ea50e35eb8f489d7378249/pywin32-312-cp315-cp315-win_amd64.whl", hash = "sha256:c2f03a0f73f804a13c2735b99392b0cd426bb4f2c4d0178e5ac966a0f21618d5", size = 7025293, upload-time = "2026-06-04T07:49:54.857Z" },
{ url = "https://files.pythonhosted.org/packages/23/ed/4532e9388e65fa16b46776ef47ad631a64eda1631884488af707666350ed/pywin32-312-cp315-cp315-win_arm64.whl", hash = "sha256:a8597d28f267b39074aef51fa593530082b39cbe5a074226096857b1fed2dfb9", size = 6840337, upload-time = "2026-06-04T07:49:57.531Z" },
]
[[package]]
name = "smmap"
version = "5.0.3"
@@ -636,6 +657,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" },
]
[[package]]
name = "wmi"
version = "1.5.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pywin32" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d4/66/6364deb0a03415f96c66803d8c4379f808f2401da3bdb183348487b10510/WMI-1.5.1.tar.gz", hash = "sha256:b6a6be5711b1b6c8d55bda7a8befd75c48c12b770b9d227d31c1737dbf0d40a6", size = 26254, upload-time = "2020-04-28T08:22:58.096Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ee/b9/a80d1ed4d115dac8e2ac08d16af046a77ab58e3d186e22395bf2add24090/WMI-1.5.1-py2.py3-none-any.whl", hash = "sha256:1d6b085e5c445141c475476000b661f60fff1aaa19f76bf82b7abb92e0ff4942", size = 28912, upload-time = "2020-04-28T08:22:56.055Z" },
]
[[package]]
name = "yarl"
version = "1.24.5"