Compare commits

...

38 Commits
v1.1.1 ... main

Author SHA1 Message Date
7ee3eb377e 修复macOS挂载点检查逻辑,确保正确处理系统和应用程序目录
Some checks failed
Compile / build (x64, windows-latest) (push) Failing after 21s
Compile / build (x64, ubuntu-latest) (push) Failing after 2m43s
2024-12-10 13:14:10 +08:00
90b4952ea9 修复macOS挂载点检查逻辑,确保正确处理根挂载点
Some checks failed
Compile / build (x64, windows-latest) (push) Failing after 17s
Compile / build (x64, ubuntu-latest) (push) Failing after 2m47s
2024-12-10 13:10:44 +08:00
0559212d03 修复macOS分区监控逻辑,确保正确处理挂载点
Some checks failed
Compile / build (x64, windows-latest) (push) Failing after 45s
Compile / build (x64, ubuntu-latest) (push) Failing after 2m39s
2024-12-10 13:07:01 +08:00
e74324e21b 修复平台名称大小写问题,确保Linux和macOS的分区监控逻辑正确
Some checks failed
Compile / build (x64, windows-latest) (push) Failing after 21s
Compile / build (x64, ubuntu-latest) (push) Failing after 2m54s
2024-12-09 00:30:34 +08:00
48ac46bbb3 增加客户端启动日志并优化线程启动逻辑
Some checks failed
Compile / build (x64, windows-latest) (push) Failing after 43s
Compile / build (x64, ubuntu-latest) (push) Failing after 2m8s
2024-12-09 00:26:16 +08:00
b5ba568a66 支持macOS的磁盘分区监控,重构观察逻辑
Some checks failed
Compile / build (x64, windows-latest) (push) Failing after 1m0s
Compile / build (x64, ubuntu-latest) (push) Failing after 4m31s
2024-12-08 23:50:45 +08:00
b30b3429ad 新增排序功能
Some checks failed
Compile / build (x64, ubuntu-latest) (push) Failing after 1m27s
Compile / build (x64, windows-latest) (push) Has been cancelled
2024-10-11 01:15:39 +08:00
7cbca85a60 修复部分linux获取发行版错误的问题
Some checks failed
Compile / build (x64, windows-latest) (push) Failing after 10s
Compile / build (x64, ubuntu-latest) (push) Failing after 15s
2024-10-06 04:39:04 +08:00
ce3a07e350 修复部分linux获取发行版错误的问题 2024-10-06 04:36:54 +08:00
9160e7c4b4 修复部分linux获取发行版错误的问题 2024-10-06 04:35:07 +08:00
8e2c5c24b8 安装脚本添加本地化 2024-10-06 04:32:12 +08:00
c63d84106b 安装脚本添加本地化 2024-10-06 02:17:41 +08:00
0d7b7700de 修复脚本执行错误 2024-10-05 23:13:33 +08:00
b5bf2c1de6 修复脚本执行错误 2024-10-05 23:08:40 +08:00
87418edeea 修复脚本执行错误
Some checks failed
Compile / build (x64, ubuntu-latest) (push) Failing after 15s
Compile / build (x64, windows-latest) (push) Failing after 21s
2024-10-05 23:01:17 +08:00
5c089873ca 修复脚本执行错误
Some checks failed
Compile / build (x64, windows-latest) (push) Failing after 14s
Compile / build (x64, ubuntu-latest) (push) Failing after 1m35s
2024-10-05 22:56:04 +08:00
79242f9dd6 修复脚本执行错误 2024-10-05 22:54:33 +08:00
22feee38ee 修复脚本执行错误
Some checks failed
Compile / build (x64, windows-latest) (push) Failing after 13s
Compile / build (x64, ubuntu-latest) (push) Failing after 3m6s
2024-10-05 22:50:41 +08:00
e0aa6adbeb 修复脚本执行错误 2024-10-05 22:48:40 +08:00
87859a58f8 修复脚本执行错误 2024-10-05 22:46:31 +08:00
d1098cf735 Merge remote-tracking branch 'origin/main' 2024-10-05 22:25:32 +08:00
62096e5f4d 修复脚本执行错误 2024-10-05 22:25:17 +08:00
26db6fe7d0
Create LICENSE 2024-10-05 22:04:50 +08:00
3b09c0650d 修复脚本执行错误 2024-10-05 22:02:53 +08:00
fb7e3144d3 修复脚本执行错误 2024-10-05 21:57:48 +08:00
c271378c71 修复脚本执行错误
Some checks failed
Compile / build (x64, windows-latest) (push) Failing after 38s
Compile / build (x64, ubuntu-latest) (push) Failing after 3m7s
2024-10-05 21:52:39 +08:00
5834abcd33 修复脚本执行错误 2024-10-05 21:50:12 +08:00
67475ef499 修复脚本执行错误 2024-10-05 21:20:22 +08:00
42c4db6c01 修复脚本执行错误 2024-10-05 21:02:50 +08:00
4af3579d70 新增Linux安装脚本 2024-10-05 20:53:58 +08:00
cd56065e32 os字段新增版本和发行版
Some checks failed
Compile / build (x64, windows-latest) (push) Failing after 19s
Compile / build (x64, ubuntu-latest) (push) Failing after 35s
2024-10-05 05:34:26 +08:00
3c960896f4 修复运行时间错误的问题
Some checks failed
Compile / build (x64, windows-latest) (push) Failing after 36s
Compile / build (x64, ubuntu-latest) (push) Failing after 1m27s
2024-10-05 03:21:33 +08:00
c2a3966f37 修复运行时间错误的问题 2024-10-05 03:21:01 +08:00
3fa94f653d 修复运行时间错误的问题
Some checks failed
Compile / build (x64, windows-latest) (push) Failing after 39s
Compile / build (x64, ubuntu-latest) (push) Failing after 2m24s
2024-10-04 08:29:49 +08:00
4cb1fcdb21 添加系统启动时间 2024-10-04 07:46:00 +08:00
657cd1a80f 添加系统启动时间 2024-10-04 07:34:28 +08:00
3d14b5a18e 添加系统启动时间 2024-10-04 07:33:16 +08:00
2c3cda6e46 排除linux下无关分区
Some checks failed
Compile / build (x64, windows-latest) (push) Failing after 20s
Compile / build (x64, ubuntu-latest) (push) Failing after 35s
2024-10-03 18:49:28 +08:00
9 changed files with 509 additions and 88 deletions

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Snowykami
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -19,27 +19,46 @@ _✨ 服务器状态 - 客户端 ✨_
服务器状态的客户端命令行工具
- 跨平台支持
- 自动上报服务器状态
- 支持自定义标签、地域、链接等信息
## 💿 安装
- 先决条件:`curl` `python3` `pip` `venv` `git`
使用 pip 安装(确保包路径在环境变量下)
- Linux 可使用脚本安装,带自动部署和自启动
pip install server-status
```shell
sudo bash -c "$(curl -sSL https://raw.githubusercontent.com/snowykami/server-status-client/refs/heads/main/deploy.sh)"
```
如果位于中国大陆无法访问GitHub可使用中国版脚本
```shell
sudo bash -c "$(curl -sSL https://git.liteyuki.icu/snowykami/server-status-client/raw/branch/main/deploy-cn.sh)"
```
Debian系请使用pipx安装
- 或手动部署
```bash
sudo apt install pipx
sudo pipx install server-status
```
若出现环境问题,请从 Actions 下载构建好的二进制文件
```shell
# 克隆仓库
git clone https://github.com/snowykami/server-status-client
cd server-status-client
# 配置环境
python3 -m venv venv
source venv/bin/activate
# 安装依赖
pip install pdm
pdm install
# 如需自启动请自行添加到系统服务
```
## 🎉 使用
### 命令
- `server-status <server> <token> <id> run` - 运行客户端
- `server-status <server> <token> <id> rm` - 从服务端移除主机
- `python main.py <server> <token> <id> run` - 运行客户端
- `python.main.py <server> <token> <id> rm` - 从服务端移除主机
#### 可选项
@ -52,47 +71,20 @@ sudo pipx install server-status
#### 示例
```shell
server_status https://status.liteyuki.icu 114514 myhost run -n "MyHost" --labels "标签1,标签2" --interval 5 --location "Chongqing" --link "https://example.com"
python main.py https://status.liteyuki.icu 114514 myhost run -n "MyHost" --labels "标签1,标签2" --interval 5 --location "Chongqing" --link "https://example.com"
```
## 📝 其他
### 开机启动
执行以下命令
```shell
sudo pipx ensurepath # 确保pipx路径在环境变量下
sudo touch /etc/systemd/system/server-status-client.service
sudo bash -c 'cat <<EOF > /etc/systemd/system/server-status-client.service
[Unit]
Description=Server Status Client
After=network-online.target
[Service]
Type=simple
ExecStart=server-status <server> <token> <id> run # 请替换为实际参数
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF'
sudo systemctl enable server-status-client
sudo systemctl start server-status-client
```
- 安装脚本已自动添加到系统服务
### 更新
```shell
git pull
sudo systemctl restart server-status-client
#
git pull
systemctl restart server-status-client
```
### 服务端

96
deploy-cn.sh Executable file
View File

@ -0,0 +1,96 @@
#!/bin/bash
# 部署脚本中国大陆可用版本
# check if sudo is used
if [ "$EUID" -ne 0 ]; then
echo "Please run as root"
exit 1
fi
# check install dir
install_dir="/opt"
echo -n "安装目录? (默认: $install_dir/server-status-client): " && read -r install_dir_input
if [ -n "$install_dir_input" ]; then
install_dir="$install_dir_input"
fi
# check server
echo -n "服务端地址? (必须): " && read -r server
if [ -z "$server" ]; then
echo "服务端地址是必须的"
exit 1
fi
# check token
echo -n "令牌? (必须或留空): " && read -r token
# check hostname
hostname=$(hostname)
echo -n "此主机名? (默认: $hostname): " && read -r hostname_input
if [ -n "$hostname_input" ]; then
hostname="$hostname_input"
fi
# labels
echo -n "标签们? (空格分隔): " && read -r labels_input
if [ -n "$labels_input" ]; then
labels="$labels_input"
fi
# location
echo -n "地理位置? (可选|自定义): " && read -r location_input
if [ -n "$location_input" ]; then
location="$location_input"
fi
repo2="https://git.liteyuki.icu/snowykami/server-status-client"
# try 1 if failed try 2
git clone "$repo2" "$install_dir/server-status-client"
cd "$install_dir/server-status-client" || { echo "克隆失败"; exit 1; }
# create venv
python3 -m venv venv
python_exe="./venv/bin/python"
# check if venv is created
if [ ! -f "$python_exe" ]; then
echo "创建虚拟环境失败"
exit 1
fi
echo "虚拟环境创建成功"
# install the required packages
echo "正在安装依赖包..."
$python_exe -m pip install pdm -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple
$python_exe -m pdm install
# create the systemd service
echo "正在创建服务..."
# generate random id
# shellcheck disable=SC2002
id=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 8 | head -n 1)
bash -c "cat <<EOF > /etc/systemd/system/server-status-client.service
[Unit]
Description=Server Status Client
After=network-online.target
[Service]
Type=simple
ExecStart=$install_dir/server-status-client/venv/bin/python main.py $server $token $id run -n $hostname --labels $labels --location $location
WorkingDirectory=$install_dir/server-status-client
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF"
# enable and start the service
systemctl enable server-status-client
systemctl start server-status-client
echo "安装完成,服务已启动"

0
deploy.cmd Normal file
View File

99
deploy.sh Executable file
View File

@ -0,0 +1,99 @@
#!/bin/bash
# check if sudo is used
if [ "$EUID" -ne 0 ]; then
echo "Please run as root"
exit 1
fi
# check install dir
install_dir="/opt"
echo -n "Install directory? (default: $install_dir/server-status-client): " && read -r install_dir_input
if [ -n "$install_dir_input" ]; then
install_dir="$install_dir_input"
fi
# check server
echo -n "Server? (required): " && read -r server
if [ -z "$server" ]; then
echo "Server is required"
exit 1
fi
# check token
echo -n "Token? (required): " && read -r token
if [ -z "$token" ]; then
echo "Token is required"
exit 1
fi
# check hostname
hostname=$(hostname)
echo -n "Hostname? (default: $hostname): " && read -r hostname_input
if [ -n "$hostname_input" ]; then
hostname="$hostname_input"
fi
# labels
echo -n "Labels? (space separated): " && read -r labels_input
if [ -n "$labels_input" ]; then
labels="$labels_input"
fi
# location
echo -n "Location? (optional): " && read -r location_input
if [ -n "$location_input" ]; then
location="$location_input"
fi
# clone repo
repo1="https://github.com/snowykami/server-status-client"
repo2="https://git.liteyuki.icu/snowykami/server-status-client"
# try 1 if failed try 2
git clone "$repo1" "$install_dir/server-status-client" || git clone "$repo2" "$install_dir/server-status-client"
cd "$install_dir/server-status-client" || { echo "Failed to clone repo"; exit 1; }
# create venv
python3 -m venv venv
python_exe="./venv/bin/python"
# check if venv is created
if [ ! -f "$python_exe" ]; then
echo "Failed to create venv"
exit 1
fi
echo "venv created successfully"
# install the required packages
echo "Installing the required packages"
$python_exe -m pip install pdm
$python_exe -m pdm install
# create the systemd service
echo "Creating the systemd service"
# generate random id
# shellcheck disable=SC2002
id=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 8 | head -n 1)
bash -c "cat <<EOF > /etc/systemd/system/server-status-client.service
[Unit]
Description=Server Status Client
After=network-online.target
[Service]
Type=simple
ExecStart=$install_dir/server-status-client/venv/bin/python main.py $server $token $id run -n $hostname --labels $labels --location $location
WorkingDirectory=$install_dir/server-status-client
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF"
# enable and start the service
systemctl enable server-status-client
systemctl start server-status-client
echo "server-status-client installed successfully"

View File

@ -9,6 +9,7 @@ dependencies = [
"requests>=2.32.3",
"psutil>=6.0.0",
"arclet-alconna>=1.8.30",
"pytz>=2024.2",
]
requires-python = ">=3.10"
readme = "README.md"
@ -30,5 +31,5 @@ tag_filter = "v*"
tag_regex = '^v(?:\D*)?(?P<version>([1-9][0-9]*!)?(0|[1-9][0-9]*)(\.(0|[1-9][0-9]*))*((a|b|c|rc)(0|[1-9][0-9]*))?(\.post(0|[1-9][0-9]*))?(\.dev(0|[1-9][0-9]*))?$)$'
[[tool.pdm.source]]
name = "tuna"
name = "pypi"
url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple"

View File

@ -6,7 +6,43 @@ from typing import Any
import psutil
import requests
excluded_partition_prefix = ("/var", "/boot", "/run", "/proc", "/sys", "/dev", "/tmp", "/snap")
from server_status.timezone import get_timezone
excluded_partition_prefix = (
"/var",
"/boot",
"/run",
"/proc",
"/sys",
"/dev",
"/tmp",
"/snap",
"/System",
"/Applications",
"/private",
"/Library",
)
include_partition_prefix_mac = ("/Volumes")
os_name = "" # linux下为发行版名称windows下为Windows macOS下为Darwin
os_version = "" # linux下为发行版版本windows下为Windows版本
try:
# read /etc/os-release
with open("/etc/os-release") as f:
os_release = f.read()
# 找到NAME=和VERSION=的行
for line in os_release.split("\n"):
if line.startswith("NAME="):
os_name = line.split("=")[1].replace('"', "")
elif line.startswith("VERSION_ID="):
os_version = line.split("=")[1].replace('"', "")
except FileNotFoundError:
os_name = platform.system()
os_version = platform.release()
print("Current OS:", os_name, os_version)
def log(*args):
@ -27,6 +63,7 @@ def get_network_speed(interval) -> tuple[int, int]:
class Hardware:
os_release: str = ""
mem_total: int = psutil.virtual_memory().total
mem_used: int = psutil.virtual_memory().used
@ -39,6 +76,8 @@ class Hardware:
disks: dict[str, dict[str, int]] = {}
timezone: str = get_timezone()
net_up: int = 0
net_down: int = 0
net_type: str = "ethernet"
@ -108,7 +147,9 @@ class Api:
"""
self.headers.update(self.format(headers))
def format(self, obj: str | list[str] | dict[str, Any]) -> str | list[str] | dict[str, Any]:
def format(
self, obj: str | list[str] | dict[str, Any]
) -> str | list[str] | dict[str, Any]:
if isinstance(obj, str):
obj = obj.format(**self.variables)
elif isinstance(obj, dict):
@ -121,8 +162,17 @@ class Api:
class Client:
def __init__(self, addr: str, token: str, client_id: str, name: str = "", location: str = "", labels: list[str] = [], link: str = "",
interval: int = 2):
def __init__(
self,
addr: str,
token: str,
client_id: str,
name: str = "",
location: str = "",
labels: list[str] = [],
link: str = "",
interval: int = 2,
):
self.api = Api(addr, {"token": token, "id": client_id})
self.api = self.api.group("/client")
self.api.add_headers(Authorization="{token}")
@ -136,15 +186,66 @@ class Client:
self.link = link
self.interval = interval
self.start_time: float = psutil.boot_time()
self.hardware = Hardware()
log("Client initialized",
f"Name: {self.name}({self.client_id}), Location: {self.location}, Labels: {self.labels}")
log(
"Client initialized",
f"Name: {self.name}({self.client_id}), Location: {self.location}, Labels: {self.labels}",
)
def start(self):
self.start_time = time.time()
self.observe()
log("Starting client")
threading.Thread(target=self._start_obs, daemon=True).start()
threading.Thread(target=self._start_post, daemon=True).start()
while True:
time.sleep(1)
def _start_obs(self):
"""启动监控记录线程"""
while True:
try:
self.hardware.mem_total = psutil.virtual_memory().total
self.hardware.mem_used = psutil.virtual_memory().used
self.hardware.swap_total = psutil.swap_memory().total
self.hardware.swap_used = psutil.swap_memory().used
self.hardware.cpu_cores = psutil.cpu_count(logical=False)
self.hardware.cpu_logics = psutil.cpu_count(logical=True)
for part in psutil.disk_partitions():
try:
usage = psutil.disk_usage(part.mountpoint)
if (
(
platform.system() in ("Linux", "Darwin")
and (
part.mountpoint.startswith(
excluded_partition_prefix
)
)
)
):
continue
self.hardware.disks[part.device] = {
"mountpoint": part.mountpoint,
"device": part.device,
"fstype": part.fstype,
"total": usage.total,
"used": usage.used,
}
except:
pass
self.hardware.cpu_percent = psutil.cpu_percent(1)
self.hardware.net_up, self.hardware.net_down = get_network_speed(1)
log("Observed")
except Exception as e:
log(f"Failed to observe: {e}")
def _start_post(self):
"""启动上报进程"""
while True:
try:
resp = self.get_ping()
@ -152,9 +253,13 @@ class Client:
log(f"Connected to server {self.addr}")
break
else:
log(f"Failed to connect to server {self.addr}, retrying in 5 seconds: {resp.text}")
log(
f"Failed to connect to server {self.addr}, retrying in 5 seconds: {resp.text}"
)
except Exception as e:
log(f"Failed to connect to server {self.addr}, retrying in 5 seconds: {e}")
log(
f"Failed to connect to server {self.addr}, retrying in 5 seconds: {e}"
)
time.sleep(5)
while True:
@ -181,14 +286,19 @@ class Client:
"id": self.client_id,
"name": self.name,
"os": {
"name": platform.system(),
"version": platform.version(),
"name": platform.system(), # 系统类型 linux|windows|darwin
"version": os_name + os_version,
# 系统版本复杂描述 #1 SMP PREEMPT_DYNAMIC Fri Sep 13 10:42:50 UTC 2024 (5c05eeb)
"machine": platform.machine(), # 机器类型 x86_64
"release": os_version, # 系统版本
},
"labels": self.labels,
"location": self.location,
"uptime": int(time.time() - self.start_time),
"start_time": int(self.start_time), # 系统启动的时间
"link": self.link,
"observed_at": int(time.time()),
"timezone": self.hardware.timezone,
},
"hardware": {
"mem": {
@ -213,39 +323,5 @@ class Client:
},
}
def observe(self):
"""
观察硬件状态并更新
Returns:
"""
def _observe():
while True:
self.hardware.mem_total = psutil.virtual_memory().total
self.hardware.mem_used = psutil.virtual_memory().used
self.hardware.swap_total = psutil.swap_memory().total
self.hardware.swap_used = psutil.swap_memory().used
self.hardware.cpu_cores = psutil.cpu_count(logical=False)
self.hardware.cpu_logics = psutil.cpu_count(logical=True)
for part in psutil.disk_partitions():
try:
usage = psutil.disk_usage(part.mountpoint)
if part.mountpoint.startswith(excluded_partition_prefix) or usage.total == 0:
continue
self.hardware.disks[part.device] = {
"total": usage.total,
"used": usage.used,
}
except:
pass
self.hardware.cpu_percent = psutil.cpu_percent(1)
self.hardware.net_up, self.hardware.net_down = get_network_speed(1)
log("Observed")
threading.Thread(target=_observe, daemon=True).start()
def remove(self, client_id) -> requests.Response:
return self.api.delete("/host", data={"id": client_id})

View File

@ -3,7 +3,7 @@ import socket
from arclet.alconna import Alconna, Subcommand, Option, Args, MultiVar
server_status_alc = Alconna(
server_status_alc = Alconna( # type: ignore
"server_status",
Args["server", str]["token", str]["id", str],
Subcommand(

136
server_status/timezone.py Normal file
View File

@ -0,0 +1,136 @@
import os
import platform
from zoneinfo import ZoneInfo
windows_timezone_map = {
"Dateline Standard Time": "Etc/GMT+12",
"UTC-11": "Etc/GMT+11",
"Aleutian Standard Time": "America/Adak",
"Hawaiian Standard Time": "Pacific/Honolulu",
"Marquesas Standard Time": "Pacific/Marquesas",
"Alaskan Standard Time": "America/Anchorage",
"UTC-09": "Etc/GMT+9",
"Pacific Standard Time (Mexico)": "America/Tijuana",
"UTC-08": "Etc/GMT+8",
"Pacific Standard Time": "America/Los_Angeles",
"US Mountain Standard Time": "America/Phoenix",
"Mountain Standard Time (Mexico)": "America/Chihuahua",
"Mountain Standard Time": "America/Denver",
"Central America Standard Time": "America/Guatemala",
"Central Standard Time": "America/Chicago",
"Easter Island Standard Time": "Pacific/Easter",
"Central Standard Time (Mexico)": "America/Mexico_City",
"Canada Central Standard Time": "America/Regina",
"SA Pacific Standard Time": "America/Bogota",
"Eastern Standard Time (Mexico)": "America/Cancun",
"Eastern Standard Time": "America/New_York",
"Haiti Standard Time": "America/Port-au-Prince",
"Cuba Standard Time": "America/Havana",
"US Eastern Standard Time": "America/Indianapolis",
"Turks And Caicos Standard Time": "America/Grand_Turk",
"Paraguay Standard Time": "America/Asuncion",
"Atlantic Standard Time": "America/Halifax",
"Venezuela Standard Time": "America/Caracas",
"Central Brazilian Standard Time": "America/Cuiaba",
"SA Western Standard Time": "America/La_Paz",
"Pacific SA Standard Time": "America/Santiago",
"SA Eastern Standard Time": "America/Cayenne",
"Argentina Standard Time": "America/Buenos_Aires",
"Greenland Standard Time": "America/Godthab",
"Montevideo Standard Time": "America/Montevideo",
"Bahia Standard Time": "America/Bahia",
"UTC-02": "Etc/GMT+2",
"Azores Standard Time": "Atlantic/Azores",
"Cape Verde Standard Time": "Atlantic/Cape_Verde",
"UTC": "Etc/GMT",
"GMT Standard Time": "Europe/London",
"Greenwich Standard Time": "Atlantic/Reykjavik",
"W. Europe Standard Time": "Europe/Berlin",
"Central Europe Standard Time": "Europe/Budapest",
"Romance Standard Time": "Europe/Paris",
"Central European Standard Time": "Europe/Warsaw",
"W. Central Africa Standard Time": "Africa/Lagos",
"Namibia Standard Time": "Africa/Windhoek",
"GTB Standard Time": "Europe/Bucharest",
"Middle East Standard Time": "Asia/Beirut",
"Egypt Standard Time": "Africa/Cairo",
"Syria Standard Time": "Asia/Damascus",
"E. Europe Standard Time": "Europe/Chisinau",
"South Africa Standard Time": "Africa/Johannesburg",
"FLE Standard Time": "Europe/Kiev",
"Turkey Standard Time": "Europe/Istanbul",
"Israel Standard Time": "Asia/Jerusalem",
"Jordan Standard Time": "Asia/Amman",
"Arabic Standard Time": "Asia/Riyadh",
"Kaliningrad Standard Time": "Europe/Kaliningrad",
"Arab Standard Time": "Asia/Riyadh",
"E. Africa Standard Time": "Africa/Nairobi",
"Iran Standard Time": "Asia/Tehran",
"Arabian Standard Time": "Asia/Dubai",
"Astrakhan Standard Time": "Europe/Astrakhan",
"Russian Standard Time": "Europe/Moscow",
"E. Europe Standard Time": "Europe/Chisinau",
"W. Australia Standard Time": "Australia/Perth",
"Moscow Standard Time": "Europe/Moscow",
"Pakistan Standard Time": "Asia/Karachi",
"India Standard Time": "Asia/Kolkata",
"Sri Lanka Standard Time": "Asia/Colombo",
"Nepal Standard Time": "Asia/Kathmandu",
"Bangladesh Standard Time": "Asia/Dhaka",
"Afghanistan Standard Time": "Asia/Kabul",
"Myanmar Standard Time": "Asia/Yangon",
"SE Asia Standard Time": "Asia/Bangkok",
"North Asia Standard Time": "Asia/Krasnoyarsk",
"China Standard Time": "Asia/Shanghai",
"Singapore Standard Time": "Asia/Singapore",
"W. Australia Standard Time": "Australia/Perth",
"Taipei Standard Time": "Asia/Taipei",
"Ulaanbaatar Standard Time": "Asia/Ulaanbaatar",
"North Asia East Standard Time": "Asia/Irkutsk",
"Korea Standard Time": "Asia/Seoul",
"Tokyo Standard Time": "Asia/Tokyo",
"Yakutsk Standard Time": "Asia/Yakutsk",
"Cen. Australia Standard Time": "Australia/Adelaide",
"AUS Central Standard Time": "Australia/Darwin",
"E. Australia Standard Time": "Australia/Brisbane",
"AUS Eastern Standard Time": "Australia/Sydney",
"West Pacific Standard Time": "Pacific/Port_Moresby",
"Tasmania Standard Time": "Australia/Hobart",
"Magadan Standard Time": "Asia/Magadan",
"Vladivostok Standard Time": "Asia/Vladivostok",
"Russia Time Zone 10": "Asia/Srednekolymsk",
"Central Pacific Standard Time": "Pacific/Guadalcanal",
"Fiji Standard Time": "Pacific/Fiji",
"New Zealand Standard Time": "Pacific/Auckland",
"UTC+12": "Etc/GMT-12",
"Kamchatka Standard Time": "Asia/Kamchatka",
"Tonga Standard Time": "Pacific/Tongatapu",
"Samoa Standard Time": "Pacific/Apia",
"Line Islands Standard Time": "Pacific/Kiritimati",
}
def get_timezone() -> str:
try:
# 尝试获取系统的本地时区
if 'TZ' in os.environ:
return os.environ['TZ']
# 如果环境变量中没有TZ尝试获取系统时区
if platform.system() == "Linux":
# Linux:
with open("/etc/timezone", "r") as f:
return f.read().strip()
elif platform.system() == "Darwin":
# macOS:
return ZoneInfo.from_file(open("/etc/localtime")).key
elif platform.system() == "Windows":
# Windows:
import winreg
with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation") as key:
tz = winreg.QueryValueEx(key, "TimeZoneKeyName")[0]
return windows_timezone_map.get(tz) or tz
else:
return "UTC"
except Exception as e:
return "UTC"