nonebot-plugin-l4d2-server 1.1.1__tar.gz → 1.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/PKG-INFO +2 -3
  2. nonebot_plugin_l4d2_server-1.1.2/nonebot_plugin_l4d2_server/config.py +131 -0
  3. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_help/__init__.py +1 -1
  4. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_image/convert.py +0 -69
  5. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_image/html_img.py +35 -17
  6. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_image/image_tools.py +1 -104
  7. nonebot_plugin_l4d2_server-1.1.2/nonebot_plugin_l4d2_server/l4_image/img/back_img/background.jpg +0 -0
  8. nonebot_plugin_l4d2_server-1.1.2/nonebot_plugin_l4d2_server/l4_image/img/template/normal.html +113 -0
  9. nonebot_plugin_l4d2_server-1.1.2/nonebot_plugin_l4d2_server/l4_image/img/template/style.css +264 -0
  10. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_request/draw_msg.py +1 -18
  11. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/pyproject.toml +2 -3
  12. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/config.py +0 -94
  13. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//344/273/213/347/273/215.png +0 -0
  14. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//344/273/273/345/212/241.png +0 -0
  15. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//344/277/241/346/201/257.png +0 -0
  16. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//345/205/254/345/221/212.png +0 -0
  17. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//345/210/200/345/211/221.png +0 -0
  18. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//345/210/207/346/215/242.png +0 -0
  19. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//345/210/240/351/231/244.png +0 -0
  20. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//345/210/267/346/226/260.png +0 -0
  21. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//345/215/241/347/273/204.png +0 -0
  22. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//345/223/252/351/207/214.png +0 -0
  23. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//345/234/260/345/233/276.png +0 -0
  24. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//345/257/274/345/205/245.png +0 -0
  25. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//345/257/274/345/207/272.png +0 -0
  26. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//345/275/261.png +0 -0
  27. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//346/213/274/345/233/276.png +0 -0
  28. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//346/216/242/347/264/242.png +0 -0
  29. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//346/216/250/351/200/201.png +0 -0
  30. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//346/224/266/351/233/206.png +0 -0
  31. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//346/224/273/347/225/245.png +0 -0
  32. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//346/233/264/346/226/260.png +0 -0
  33. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//346/235/220/346/226/231.png +0 -0
  34. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//346/237/245/350/257/242.png +0 -0
  35. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//346/240/241/351/252/214.png +0 -0
  36. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//346/257/217/346/234/210.png +0 -0
  37. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//346/267/261/346/270/212.png +0 -0
  38. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//346/267/273/345/212/240.png +0 -0
  39. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//346/270/205/351/231/244.png +0 -0
  40. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//347/212/266/346/200/201.png +0 -0
  41. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//347/255/276/345/210/260.png +0 -0
  42. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//347/273/221/345/256/232.png +0 -0
  43. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//350/241/250.png +0 -0
  44. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//350/241/250/346/203/205.png +0 -0
  45. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//350/247/222/350/211/262.png +0 -0
  46. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//350/256/260/345/275/225.png +0 -0
  47. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//351/205/215/347/275/256.png +0 -0
  48. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_help/icon//351/207/215/345/220/257.png +0 -0
  49. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_image/img/template/Bocchi_The_Rock.html +0 -299
  50. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_image/img/template/Bocchi_The_Rock.png +0 -0
  51. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_image/img/template/Pixel.html +0 -341
  52. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_image/img/template/Pixel.png +0 -0
  53. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_image/img/template/Rainbow.html +0 -355
  54. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_image/img/template/Rainbow.png +0 -0
  55. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_image/img/template/fingerprint.svg +0 -15
  56. nonebot_plugin_l4d2_server-1.1.1/nonebot_plugin_l4d2_server/l4_image/img/template/normal.html +0 -205
  57. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/LICENSE +0 -0
  58. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/README.md +0 -0
  59. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/__init__.py +0 -0
  60. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/__main__.py +0 -0
  61. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/data/font/loli.ttf +0 -0
  62. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_help/Help.json +0 -0
  63. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_help/draw.py +0 -0
  64. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_help/texture2d/badge.png +0 -0
  65. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_help/texture2d/banner.png +0 -0
  66. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_help/texture2d/bg.jpg +0 -0
  67. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_help/texture2d/button.png +0 -0
  68. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_help/texture2d/icon.png +0 -0
  69. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_image/__init__.py +0 -0
  70. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_image/download.py +0 -0
  71. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_image/img/anne/anne.html +0 -0
  72. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_image/img/anne/back.png +0 -0
  73. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_image/img/anne/back1.jpg +0 -0
  74. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_image/img/anne/group_ip.html +0 -0
  75. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_image/img/anne/ip.html +0 -0
  76. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_image/img/head/head.png +0 -0
  77. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_image/img/header/logo.png +0 -0
  78. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_image/img/header/player1.jpg +0 -0
  79. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_image/img/template/HYPixel11pxU-2.ttf +0 -0
  80. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_image/img/template/Tutumianhuatang-Bold-2.ttf +0 -0
  81. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_image/img/template/bilibili.svg +0 -0
  82. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_image/img/template/github.svg +0 -0
  83. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_image/img/template/l.svg +0 -0
  84. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_image/img/template/m.svg +0 -0
  85. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_image/img/template/normal_.html +0 -0
  86. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_image/img/template/vac.png +0 -0
  87. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_image/img/template/vac_white.png +0 -0
  88. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_image/img/template/vue.css +0 -0
  89. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_image/img/template/w.svg +0 -0
  90. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_image/model.py +0 -0
  91. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_image/vtfs.py +0 -0
  92. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_local/__init__.py +0 -0
  93. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_local/file.py +0 -0
  94. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_request/__init__.py +0 -0
  95. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/l4_request/utils.py +0 -0
  96. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/utils/api/api.py +0 -0
  97. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/utils/api/models.py +0 -0
  98. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/utils/api/request.py +0 -0
  99. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/utils/api/utils.py +0 -0
  100. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/utils/database/models.py +0 -0
  101. {nonebot_plugin_l4d2_server-1.1.1 → nonebot_plugin_l4d2_server-1.1.2}/nonebot_plugin_l4d2_server/utils/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nonebot-plugin-l4d2-server
3
- Version: 1.1.1
3
+ Version: 1.1.2
4
4
  Summary: L4D2 server related operations plugin for NoneBot2
5
5
  Keywords: steam,game,l4d2,nonebot2,plugin
6
6
  Author-Email: Agnes_Digital <Z735803792@163.com>
@@ -24,9 +24,8 @@ Requires-Dist: nonebot_plugin_datastore>=1.3.0
24
24
  Requires-Dist: nonebot-plugin-tortoise-orm>=0.1.4
25
25
  Requires-Dist: aiohttp>=3.8.4
26
26
  Requires-Dist: jinja2>=3.0.0
27
- Requires-Dist: srctools>=2.3.9
27
+ Requires-Dist: srctools>=2.6.0
28
28
  Requires-Dist: httpx>=0.22.0
29
- Requires-Dist: msgspec>=0.18.0
30
29
  Requires-Dist: python-a2s>=1.4.1
31
30
  Requires-Dist: ujson>=5.10.0
32
31
  Requires-Dist: lxml>=5.2.2
@@ -0,0 +1,131 @@
1
+ from pathlib import Path
2
+ from typing import List
3
+
4
+ from nonebot import get_plugin_config
5
+ from nonebot.log import logger
6
+ from pydantic import BaseModel, Field
7
+
8
+ # 常量定义
9
+ DATAPATH = Path(__file__).parent.joinpath("data")
10
+ DEFAULT_DATA_DIR = "data/L4D2"
11
+ DEFAULT_FONT = str(Path(__file__).parent.joinpath("data/font/loli.ttf"))
12
+
13
+
14
+ # 初始化数据目录
15
+ def init_data_directory(data_dir: Path) -> None:
16
+ """初始化数据目录和必要文件"""
17
+ data_dir.mkdir(parents=True, exist_ok=True)
18
+ json_file = data_dir / "l4d2.json"
19
+
20
+ if not json_file.exists():
21
+ logger.info(f"文件 {json_file.name} 不存在,已创建并初始化为 {{}}")
22
+ json_file.write_text("{}", encoding="utf-8")
23
+
24
+
25
+ # 初始化目录结构
26
+ DATAOUT = Path(DEFAULT_DATA_DIR)
27
+ init_data_directory(DATAOUT)
28
+ init_data_directory(DATAOUT / "l4d2")
29
+ server_all_path = DATAOUT / "l4d2"
30
+ server_all_path.mkdir(parents=True, exist_ok=True)
31
+
32
+ ICONPATH = DATAPATH / "icon"
33
+ global map_index
34
+ map_index = 0
35
+
36
+
37
+ class ConfigModel(BaseModel):
38
+ """插件配置模型"""
39
+
40
+ l4_enable: bool = Field(default=True, description="是否全局启用求生功能")
41
+ l4_image: bool = Field(default=True, description="是否启用图片")
42
+ l4_connect: bool = Field(default=True, description="是否在查服命令后加入connect ip")
43
+ l4_path: str = Field(default=DEFAULT_DATA_DIR, description="插件数据路径")
44
+ l4_players: int = Field(default=4, ge=1, description="查询总图时展示的用户数量")
45
+ l4_style: str = Field(default="default", description="图片风格")
46
+ l4_font: str = Field(default=DEFAULT_FONT, description="字体文件路径")
47
+ l4_show_ip: bool = Field(
48
+ default=True,
49
+ description="单服务器查询时是否展示ip直连地址",
50
+ )
51
+ l4_local: List[str] = Field(default_factory=list, description="本地服务器路径列表")
52
+
53
+ @classmethod
54
+ def validate_players(cls, v):
55
+ if v < 1:
56
+ raise ValueError("玩家数量必须大于0")
57
+ return v
58
+
59
+ @classmethod
60
+ def validate_font(cls, v):
61
+ if not Path(v).exists():
62
+ logger.warning(f"字体文件 {v} 不存在")
63
+ return v
64
+
65
+ @classmethod
66
+ def validate_local_paths(cls, v):
67
+ path = Path(v)
68
+ if not (path / "steam_appid.txt").exists():
69
+ raise ValueError(f"路径 {v} 下缺少 steam_appid.txt 文件")
70
+ return str(path.resolve())
71
+
72
+
73
+ config = get_plugin_config(ConfigModel)
74
+
75
+
76
+ class ConfigManager:
77
+ """配置项管理类,提供类型安全的配置更新方法"""
78
+
79
+ def __init__(self):
80
+ self._config = config
81
+
82
+ @property
83
+ def current_config(self) -> ConfigModel:
84
+ """获取当前配置"""
85
+ return self._config
86
+
87
+ def update_image_config(self, enabled: bool) -> None:
88
+ """更新图片配置"""
89
+ self._config.l4_image = enabled
90
+
91
+ def update_style_config(self, style: str) -> None:
92
+ """更新图片风格配置"""
93
+ if not isinstance(style, str):
94
+ raise TypeError("style必须是字符串")
95
+ self._config.l4_style = style
96
+
97
+ def update_connect_config(self, enabled: bool) -> None:
98
+ """更新connect ip配置"""
99
+ self._config.l4_connect = enabled
100
+
101
+ def update(self, **kwargs) -> None:
102
+ """
103
+ 通用配置更新方法
104
+
105
+ Args:
106
+ **kwargs: 要更新的配置项键值对
107
+
108
+ Raises:
109
+ ValueError: 当传入无效的配置项或值不合法时
110
+ TypeError: 当传入值的类型不正确时
111
+ """
112
+ valid_keys = ConfigModel.model_fields.keys()
113
+
114
+ for key, value in kwargs.items():
115
+ if key not in valid_keys:
116
+ raise ValueError(f"无效的配置项: {key}")
117
+
118
+ field_type = ConfigModel[key].type_
119
+ if not isinstance(value, field_type):
120
+ raise TypeError(f"{key} 必须是 {field_type.__name__} 类型")
121
+
122
+ setattr(self._config, key, value)
123
+
124
+ # 验证更新后的配置
125
+ try:
126
+ self._config = ConfigModel(**self._config.dict())
127
+ except ValueError as e:
128
+ logger.error(f"配置更新失败: {e!s}")
129
+
130
+
131
+ config_manager = ConfigManager()
@@ -10,7 +10,7 @@ from ..l4_image.convert import core_font
10
10
  from ..l4_image.model import PluginHelp
11
11
  from .draw import get_help
12
12
 
13
- __version__ = "1.1.1"
13
+ __version__ = "1.1.2"
14
14
  TEXT_PATH = Path(__file__).parent / "texture2d"
15
15
  HELP_DATA = Path(__file__).parent / "Help.json"
16
16
 
@@ -79,75 +79,6 @@ async def convert_img(
79
79
  return f"base64://{b64encode(img).decode()}"
80
80
 
81
81
 
82
- def convert_img_sync(img_path: Path):
83
- with img_path.open("rb") as fp:
84
- img = fp.read()
85
-
86
- return f"base64://{b64encode(img).decode()}"
87
-
88
-
89
- async def str_lenth(r: str, size: int, limit: int = 540) -> str: # noqa: RUF029
90
- result = ""
91
- temp = 0
92
- for i in r:
93
- if i == "\n":
94
- temp = 0
95
- result += i
96
- continue
97
-
98
- if temp >= limit:
99
- result += "\n" + i
100
- temp = 0
101
- else:
102
- result += i
103
-
104
- if i.isdigit():
105
- temp += round(size / 10 * 6)
106
- elif i == "/":
107
- temp += round(size / 10 * 2.2)
108
- elif i == ".":
109
- temp += round(size / 10 * 3)
110
- elif i == "%":
111
- temp += round(size / 10 * 9.4)
112
- else:
113
- temp += size
114
- return result
115
-
116
-
117
- def get_str_size(
118
- r: str,
119
- font: ImageFont.FreeTypeFont,
120
- limit: int = 540,
121
- ) -> str:
122
- result = ""
123
- line = ""
124
- for i in r:
125
- if i == "\n":
126
- result += f"{line}\n"
127
- line = ""
128
- continue
129
-
130
- line += i
131
-
132
- if hasattr(font, "getsize"):
133
- size, _ = font.getsize(line) # type: ignore
134
- else:
135
- bbox = font.getbbox(line)
136
- size, _ = bbox[2] - bbox[0], bbox[3] - bbox[1]
137
-
138
- if size >= limit:
139
- result += f"{line}\n"
140
- line = ""
141
- else:
142
- result += line
143
- return result
144
-
145
-
146
- def get_height(content: str, size: int) -> int:
147
- line_count = content.count("\n")
148
- return (line_count + 1) * size
149
-
150
-
151
82
  async def text2pic(text: str, max_size: int = 800, font_size: int = 24):
152
83
  if text.endswith("\n"):
153
84
  text = text[:-1]
@@ -1,4 +1,3 @@
1
- import random
2
1
  from pathlib import Path
3
2
  from typing import List, Optional
4
3
 
@@ -46,12 +45,20 @@ async def server_ip_pic(server_dict: List[OutServer]):
46
45
  reverse=True,
47
46
  )[:max_number]
48
47
  logger.debug(sorted_players)
48
+
49
+ # 时间转换
50
+ max_duration_len = max(
51
+ [len(str(await convert_duration(i.duration))) for i in sorted_players],
52
+ )
53
+ for player in sorted_players:
54
+ chines_dur = await convert_duration(player.duration)
55
+ dur = "{:^{}}".format(chines_dur, max_duration_len)
56
+ player.name = str(player.name) + " | " + dur
57
+
49
58
  server_info["player"] = sorted_players
50
59
  else:
51
60
  server_info["player"] = []
52
61
 
53
- # server_info["server"].server_type= f"{server_info['server'].server_type}.svg"
54
- print(server_dict)
55
62
  pic = await get_server_img(server_dict)
56
63
  if pic:
57
64
  logger.success("正在输出图片")
@@ -62,23 +69,13 @@ async def server_ip_pic(server_dict: List[OutServer]):
62
69
 
63
70
  async def get_server_img(plugins: List[OutServer]) -> Optional[bytes]:
64
71
  try:
65
- if config.l4_style == "孤独摇滚":
66
- template = env.get_template("Bocchi_The_Rock.html")
67
- elif config.l4_style == "电玩像素":
68
- template = env.get_template("Pixel.html")
69
- elif config.l4_style == "缤纷彩虹":
70
- template = env.get_template("Rainbow.html")
71
- elif config.l4_style == "随机":
72
- html_files = [
73
- str(f.name) for f in template_path.rglob("*.html") if f.is_file()
74
- ]
75
- template = env.get_template(random.choice(html_files))
76
- else:
77
- template = env.get_template("normal.html")
72
+ template = env.get_template("normal.html")
78
73
  content = await template.render_async(
79
74
  servers=plugins,
80
75
  max_count=config.l4_players,
81
76
  )
77
+ # with open("test.html", "w", encoding="utf-8") as f:
78
+ # f.write(content)
82
79
  return await html_to_pic(
83
80
  content,
84
81
  wait=0,
@@ -87,4 +84,25 @@ async def get_server_img(plugins: List[OutServer]) -> Optional[bytes]:
87
84
  )
88
85
  except Exception as e:
89
86
  logger.warning(f"Error in get_server_img: {e}")
90
- return None
87
+ return None
88
+
89
+
90
+ async def convert_duration(duration: float) -> str:
91
+ """将秒数转换为易读的时间字符串格式(例如 '1h 30m 15s')
92
+
93
+ 参数:
94
+ duration: 秒数
95
+
96
+ 返回:
97
+ 格式化的时间字符串
98
+ """
99
+ total_seconds = int(duration)
100
+ minutes, seconds = divmod(total_seconds, 60)
101
+ hours, minutes = divmod(minutes, 60)
102
+ time_str = ""
103
+ if hours > 0:
104
+ time_str += f"{hours}h "
105
+ if minutes > 0:
106
+ time_str += f"{minutes}m "
107
+ time_str += f"{seconds}s"
108
+ return time_str
@@ -6,83 +6,17 @@ from typing import Optional, Tuple, Union
6
6
 
7
7
  import httpx
8
8
  from httpx import get
9
- from PIL import Image, ImageDraw, ImageFilter, ImageFont
9
+ from PIL import Image, ImageDraw, ImageFont
10
10
 
11
11
  TEXT_PATH = Path(__file__).parent / "texture2d"
12
12
  BG_PATH = Path(__file__).parents[1] / "default_bg"
13
13
 
14
14
 
15
- def get_div():
16
- return Image.open(TEXT_PATH / "div.png")
17
-
18
-
19
15
  async def sget(url: str):
20
16
  async with httpx.AsyncClient(timeout=None) as client: # noqa: S113
21
17
  return await client.get(url=url)
22
18
 
23
19
 
24
- def get_status_icon(status: Union[int, bool]) -> Image.Image:
25
- if status:
26
- img = Image.open(TEXT_PATH / "yes.png")
27
- else:
28
- img = Image.open(TEXT_PATH / "no.png")
29
- return img
30
-
31
-
32
- def get_v4_footer():
33
- return Image.open(TEXT_PATH / "footer.png")
34
-
35
-
36
- def get_v4_bg(w: int, h: int, is_dark: bool = False, is_blur: bool = False):
37
- ci_img = CustomizeImage(BG_PATH)
38
- img = ci_img.get_image(None, w, h)
39
- if is_blur:
40
- img = img.filter(ImageFilter.GaussianBlur(radius=20))
41
- if is_dark:
42
- black_img = Image.new("RGBA", (w, h), (0, 0, 0, 180))
43
- img.paste(black_img, (0, 0), black_img)
44
- return img.convert("RGBA")
45
-
46
-
47
- async def shift_image_hue(
48
- img: Image.Image,
49
- angle: float = 30,
50
- ) -> Image.Image: # noqa: RUF029
51
- alpha = img.getchannel("A")
52
- img = img.convert("HSV")
53
-
54
- pixels = img.load()
55
- assert pixels is not None
56
- hue_shift = angle
57
-
58
- for y in range(img.height):
59
- for x in range(img.width):
60
- h, s, v = pixels[x, y] # type: ignore
61
- h = (h + hue_shift) % 360
62
- pixels[x, y] = (h, s, v) # type: ignore
63
-
64
- img = img.convert("RGBA")
65
- img.putalpha(alpha)
66
- return img
67
-
68
-
69
- async def get_pic(url: str, size: Optional[Tuple[int, int]] = None) -> Image.Image:
70
- """
71
- 从网络获取图片, 格式化为RGBA格式的指定尺寸
72
- """
73
- async with httpx.AsyncClient(timeout=None) as client: # noqa: S113
74
- resp = await client.get(url=url)
75
- if resp.status_code != 200:
76
- if size is None:
77
- size = (960, 600)
78
- return Image.new("RGBA", size)
79
- pic = Image.open(BytesIO(resp.read()))
80
- pic = pic.convert("RGBA")
81
- if size is not None:
82
- pic = pic.resize(size)
83
- return pic
84
-
85
-
86
20
  def draw_center_text_by_line(
87
21
  img: ImageDraw.ImageDraw,
88
22
  pos: Tuple[int, int],
@@ -191,43 +125,6 @@ def draw_text_by_line(
191
125
  return y
192
126
 
193
127
 
194
- def easy_paste(
195
- im: Image.Image,
196
- im_paste: Image.Image,
197
- pos=(0, 0), # noqa: ANN001
198
- direction="lt", # noqa: ANN001
199
- ):
200
- """
201
- inplace method
202
- 快速粘贴, 自动获取被粘贴图像的坐标。
203
- pos应当是粘贴点坐标,direction指定粘贴点方位,例如lt为左上
204
- """
205
- x, y = pos
206
- size_x, size_y = im_paste.size
207
- if "d" in direction:
208
- y = y - size_y
209
- if "r" in direction:
210
- x = x - size_x
211
- if "c" in direction:
212
- x = x - int(0.5 * size_x)
213
- y = y - int(0.5 * size_y)
214
- im.paste(im_paste, (x, y, x + size_x, y + size_y), im_paste)
215
-
216
-
217
- def easy_alpha_composite(
218
- im: Image.Image,
219
- im_paste: Image.Image,
220
- pos=(0, 0), # noqa: ANN001
221
- direction="lt", # noqa: ANN001
222
- ) -> Image.Image:
223
- """
224
- 透明图像快速粘贴
225
- """
226
- base = Image.new("RGBA", im.size)
227
- easy_paste(base, im_paste, pos, direction)
228
- return Image.alpha_composite(im, base)
229
-
230
-
231
128
  async def get_qq_avatar(
232
129
  qid: Optional[Union[int, str]] = None,
233
130
  avatar_url: Optional[str] = None,
@@ -0,0 +1,113 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <title>已加载服务器</title>
7
+ <link rel="stylesheet" href="style.css">
8
+ <style>
9
+ body {
10
+ margin: 0;
11
+ font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
12
+ background: url('../back_img/background.jpg') repeat;
13
+ background-size: cover;
14
+ /* filter: blur(12px) brightness(0.6); */
15
+ }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <div class="outer-frame">
21
+ <div class="container">
22
+ <h1>已加载服务器 {{ servers[1].command }} (共 {{ servers|length }} 台)</h1>
23
+
24
+ <div class="server-grid">
25
+ {% for server in servers %}
26
+ <div class="server-card">
27
+ <div class="server-name" title="{{ server.server.server_name }}">
28
+ {{ server.server.server_name|replace('\n', ' ')|replace(' ', ' ')|trim }}
29
+ </div>
30
+
31
+ <div class="map-name">
32
+ <span>🌎️</span>
33
+ <span>{{ server.server.map_name }}</span>
34
+ <label class="switch">
35
+ <input class="checkbox" type="checkbox" {% if server.enabled %} checked {% endif %} />
36
+ <span
37
+ class="slider round {% if server.server.player_count >= server.server.max_players %} locked {% endif %}">
38
+ <span
39
+ class="lock {% if server.server.player_count >= server.server.max_players %} locked {% endif %}"></span>
40
+ <span
41
+ class="status_text {% if server.server.max_players == 0 %}error{% elif server.server.player_count / server.server.max_players < 0.5 %}lack{% elif server.server.player_count >= server.server.max_players %}full{% else %}not_full{% endif %}">
42
+ {% if server.server.max_players == 0 %}
43
+ 错误
44
+ {% elif server.server.player_count / server.server.max_players < 0.5 %} 缺人 {% elif
45
+ server.server.player_count>= server.server.max_players
46
+ %}
47
+ 已满
48
+ {% else %}
49
+ 未满
50
+ {% endif %}
51
+ </span>
52
+ </span>
53
+ </label>
54
+ </div>
55
+
56
+ <div class="player-list">
57
+ {% set player_count = server.player|length %}
58
+ {% for i in range(max_count) %}
59
+ {% set player = server.player[i] if i < player_count else None %} {% if player and player.name
60
+ %} <div class="player-item">
61
+ <span class="player-score">【{{ player.score }}】</span>
62
+ <span class="player-name">{{ player.name }}</span>
63
+
64
+ </div>
65
+ {% endif %}
66
+ {% endfor %}
67
+
68
+ <div class="players">
69
+ <span class="status-indicator">
70
+ {% if server.server.player_count >= 0 %}
71
+ {% if server.server.max_players == 0 %}
72
+ <span class="offline">🔴</span>
73
+ {% elif server.server.player_count / server.server.max_players < 0.5 %} <span
74
+ class="online">🟢</span>
75
+ {% elif server.server.player_count >= server.server.max_players %}
76
+ <span class="warning">🟡</span>
77
+ {% else %}
78
+ <span class="online">🔵</span>
79
+ {% endif %}
80
+ <span class="player-count">{{ server.server.player_count }}/{{ server.server.max_players
81
+ }}</span>
82
+ {% else %}
83
+ <span class="offline">🔴 错误</span>
84
+ {% endif %}
85
+ </span>
86
+
87
+ <span class="server-meta">
88
+ <span class="server-icon">
89
+ <img src="{{ server.server.platform }}.svg" alt="平台">
90
+ </span>
91
+ {% if server.server.vac_enabled %}
92
+ <span class="server-icon">
93
+ <img src="vac.png" alt="VAC保护">
94
+ </span>
95
+ {% endif %}
96
+ <span class="ping-value">
97
+ {{ (server.server.ping * 1000)|round(1) }} ms
98
+ </span>
99
+ </span>
100
+ </div>
101
+ </div>
102
+ </div>
103
+ {% endfor %}
104
+ </div>
105
+
106
+ <div class="footer">
107
+ @Anges Digital & Nonebot2
108
+ </div>
109
+ </div>
110
+ </div>
111
+ </body>
112
+
113
+ </html>