nonebot-plugin-l4d2-server 1.0.1__py3-none-any.whl → 1.0.3__py3-none-any.whl
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.
- nonebot_plugin_l4d2_server/__main__.py +56 -34
- nonebot_plugin_l4d2_server/config.py +4 -0
- nonebot_plugin_l4d2_server/l4_help/__init__.py +1 -1
- nonebot_plugin_l4d2_server/l4_local/__init__.py +3 -1
- nonebot_plugin_l4d2_server/l4_request/__init__.py +164 -90
- nonebot_plugin_l4d2_server/l4_request/draw_msg.py +7 -1
- nonebot_plugin_l4d2_server/utils/api/request.py +33 -9
- {nonebot_plugin_l4d2_server-1.0.1.dist-info → nonebot_plugin_l4d2_server-1.0.3.dist-info}/METADATA +8 -3
- {nonebot_plugin_l4d2_server-1.0.1.dist-info → nonebot_plugin_l4d2_server-1.0.3.dist-info}/RECORD +12 -12
- {nonebot_plugin_l4d2_server-1.0.1.dist-info → nonebot_plugin_l4d2_server-1.0.3.dist-info}/WHEEL +1 -1
- {nonebot_plugin_l4d2_server-1.0.1.dist-info → nonebot_plugin_l4d2_server-1.0.3.dist-info}/entry_points.txt +0 -0
- {nonebot_plugin_l4d2_server-1.0.1.dist-info → nonebot_plugin_l4d2_server-1.0.3.dist-info}/licenses/LICENSE +0 -0
@@ -18,6 +18,7 @@
|
|
18
18
|
from pathlib import Path
|
19
19
|
from typing import TYPE_CHECKING, List, Optional
|
20
20
|
|
21
|
+
import aiofiles
|
21
22
|
import ujson as json
|
22
23
|
from nonebot.adapters import Message
|
23
24
|
from nonebot.log import logger
|
@@ -35,7 +36,7 @@ from .l4_request import (
|
|
35
36
|
get_ip_server,
|
36
37
|
get_server_detail,
|
37
38
|
reload_ip,
|
38
|
-
tj_request
|
39
|
+
tj_request,
|
39
40
|
)
|
40
41
|
from .utils.api.request import L4API
|
41
42
|
|
@@ -54,6 +55,8 @@ l4_find_player = on_command("l4find", aliases={"l4查找"})
|
|
54
55
|
ld_tj = on_command("tj", aliases={"探监"})
|
55
56
|
ld_zl = on_command("zl")
|
56
57
|
ld_kl = on_command("kl")
|
58
|
+
config_path = Path(config.l4_path) / "config.json"
|
59
|
+
|
57
60
|
|
58
61
|
@l4_help.handle()
|
59
62
|
async def _(matcher: Matcher):
|
@@ -102,17 +105,27 @@ async def _(
|
|
102
105
|
async def _(
|
103
106
|
args: Message = CommandArg(),
|
104
107
|
):
|
108
|
+
# 以后有时间补img格式
|
105
109
|
msg: str = args.extract_plain_text().strip()
|
106
110
|
tag_list: List[str] = msg.split(" ", maxsplit=1)
|
107
|
-
if len(tag_list)
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
111
|
+
if len(tag_list) == 1:
|
112
|
+
await UniMessage.text("未设置组,正在全服查找,时间较长").send()
|
113
|
+
name = tag_list[0]
|
114
|
+
out: List[OutServer] = await get_server_detail(is_img=False) # type: ignore
|
115
|
+
out_msg = "未找到玩家"
|
116
|
+
for one in out:
|
117
|
+
for player in one["player"]:
|
118
|
+
if name in player.name:
|
119
|
+
out_msg = await get_ip_server(f"{one['host']}:{one['port']}")
|
120
|
+
if len(tag_list) == 2:
|
121
|
+
group, name = tag_list
|
122
|
+
await UniMessage.text(f"正在查询{group}组").send()
|
123
|
+
out: List[OutServer] = await get_server_detail(group, is_img=False) # type: ignore
|
124
|
+
out_msg = "未找到玩家"
|
125
|
+
for one in out:
|
126
|
+
for player in one["player"]:
|
127
|
+
if name in player.name:
|
128
|
+
out_msg = await get_ip_server(f"{one['host']}:{one['port']}")
|
116
129
|
|
117
130
|
return await UniMessage.text(out_msg).finish()
|
118
131
|
|
@@ -149,18 +162,18 @@ async def _(args: Message = CommandArg()):
|
|
149
162
|
logger.info(f"重载{tag}的ip")
|
150
163
|
await L4API.get_sourceban(tag, url)
|
151
164
|
await UniMessage.text("重载ip完成").finish()
|
152
|
-
|
165
|
+
|
153
166
|
|
154
167
|
l4_add_ban = on_command("l4addban", aliases={"l4添加ban"})
|
168
|
+
|
169
|
+
|
155
170
|
@l4_add_ban.handle()
|
156
171
|
async def _(args: Message = CommandArg()):
|
157
172
|
arg = args.extract_plain_text().strip().split(" ")
|
158
|
-
|
173
|
+
|
159
174
|
if len(arg) != 2:
|
160
175
|
await UniMessage.text("请在命令后增加响应指令名和网址").finish()
|
161
|
-
|
162
|
-
config_path = Path(config.l4_path) / "config.json"
|
163
|
-
|
176
|
+
|
164
177
|
if not config_path.is_file():
|
165
178
|
config_data = {}
|
166
179
|
else:
|
@@ -169,61 +182,70 @@ async def _(args: Message = CommandArg()):
|
|
169
182
|
config_data = json.load(f)
|
170
183
|
except (json.JSONDecodeError, FileNotFoundError):
|
171
184
|
config_data = {}
|
172
|
-
|
185
|
+
|
173
186
|
config_data.update({arg[0]: arg[1]})
|
174
|
-
|
187
|
+
|
175
188
|
try:
|
176
189
|
with config_path.open("w") as f:
|
177
190
|
json.dump(config_data, f, ensure_ascii=False, indent=4)
|
178
191
|
except IOError as e:
|
179
192
|
await UniMessage.text(f"文件写入失败: {e}").finish()
|
180
|
-
|
193
|
+
|
181
194
|
await L4API.get_sourceban(arg[0], arg[1])
|
182
195
|
await UniMessage.text(f"添加成功\n组名: {arg[0]}\n网址: {arg[1]}").finish()
|
183
|
-
|
196
|
+
|
184
197
|
|
185
198
|
l4_del_ban = on_command("l4delban", aliases={"l4删除ban", "l4移除ban"})
|
199
|
+
|
200
|
+
|
186
201
|
@l4_del_ban.handle()
|
187
|
-
async def _(args: Message = CommandArg()):
|
202
|
+
async def _(args: Message = CommandArg()):
|
188
203
|
arg = args.extract_plain_text().strip().split(" ")
|
189
|
-
if
|
204
|
+
if len(arg) not in [1, 2]:
|
190
205
|
await UniMessage.text("请在命令后增加响应指令名或者带响应网址").finish()
|
191
206
|
elif len(arg) == 1:
|
192
207
|
if not Path(Path(config.l4_path) / "config.json").is_file():
|
193
208
|
await UniMessage.text("没有添加过组名").finish()
|
194
209
|
else:
|
195
|
-
with (Path(config.l4_path) / "config.json").open(
|
210
|
+
with (Path(config.l4_path) / "config.json").open(
|
211
|
+
"r",
|
212
|
+
encoding="utf-8",
|
213
|
+
) as f:
|
196
214
|
content = f.read().strip()
|
197
|
-
config_data = json.loads(content)
|
215
|
+
config_data = json.loads(content)
|
198
216
|
if arg[0] not in config_data:
|
199
217
|
await UniMessage.text("没有添加过这个组").finish()
|
200
218
|
else:
|
201
219
|
del config_data[arg[0]]
|
202
|
-
|
203
|
-
|
204
|
-
|
220
|
+
async with aiofiles.open(config_path, "w", encoding="utf-8") as f:
|
221
|
+
json.dump(config_data, f, ensure_ascii=False, indent=4)
|
222
|
+
await UniMessage.text(f"删除成功,组名:{arg[0]}").finish()
|
205
223
|
elif len(arg) == 2:
|
206
224
|
if not Path(Path(config.l4_path) / "config.json").is_file():
|
207
225
|
await UniMessage.text("没有添加过组名").finish()
|
208
226
|
else:
|
209
|
-
with (Path(config.l4_path) / "config.json").open(
|
227
|
+
with (Path(config.l4_path) / "config.json").open(
|
228
|
+
"r",
|
229
|
+
encoding="utf-8",
|
230
|
+
) as f:
|
210
231
|
content = f.read().strip()
|
211
232
|
config_datas = json.loads(content)
|
212
233
|
if arg[0] not in config_datas:
|
213
234
|
await UniMessage.text("没有添加过这个组").finish()
|
214
235
|
else:
|
215
236
|
config_datas[arg[0]] = arg[1]
|
216
|
-
with
|
217
|
-
json.dump(config_datas, f,ensure_ascii=False,indent=4)
|
237
|
+
async with aiofiles.open(config_path, "w", encoding="utf-8") as f:
|
238
|
+
json.dump(config_datas, f, ensure_ascii=False, indent=4)
|
218
239
|
await UniMessage.text(f"修改成功,组名:{arg[0]},网址:{arg[1]}").finish()
|
219
|
-
|
220
|
-
|
240
|
+
|
241
|
+
|
221
242
|
@ld_tj.handle()
|
222
243
|
async def _(matcher: Matcher):
|
223
244
|
await matcher.send("正在寻找牢房信息")
|
224
|
-
await matcher.finish(await tj_request("云","tj"))
|
225
|
-
|
245
|
+
await matcher.finish(await tj_request("云", "tj"))
|
246
|
+
|
247
|
+
|
226
248
|
@ld_zl.handle()
|
227
249
|
async def _(matcher: Matcher):
|
228
250
|
await matcher.send("正在寻找牢房信息")
|
229
|
-
await matcher.finish(await tj_request("云","zl"))
|
251
|
+
await matcher.finish(await tj_request("云", "zl"))
|
@@ -1,10 +1,14 @@
|
|
1
1
|
from pathlib import Path
|
2
2
|
|
3
3
|
from nonebot import get_plugin_config
|
4
|
+
from nonebot.log import logger
|
4
5
|
from pydantic import BaseModel
|
5
6
|
|
6
7
|
DATAPATH = Path(__file__).parent.joinpath("data")
|
7
8
|
DATAOUT = Path("data/L4D2")
|
9
|
+
if not Path(DATAOUT / "l4d2.json").exists():
|
10
|
+
logger.info("文件 l4d2.json 不存在,已创建并初始化为 {}")
|
11
|
+
Path(DATAOUT / "l4d2.json").write_text("{}", encoding="utf-8")
|
8
12
|
print(DATAOUT.absolute())
|
9
13
|
server_all_path = DATAOUT / "l4d2"
|
10
14
|
server_all_path.mkdir(parents=True, exist_ok=True)
|
@@ -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.0.
|
13
|
+
__version__ = "1.0.3"
|
14
14
|
TEXT_PATH = Path(__file__).parent / "texture2d"
|
15
15
|
HELP_DATA = Path(__file__).parent / "Help.json"
|
16
16
|
|
@@ -13,7 +13,9 @@ from .file import updown_l4d2_vpk
|
|
13
13
|
try:
|
14
14
|
vpk_path = config.l4_local[map_index]
|
15
15
|
except IndexError:
|
16
|
-
logger.warning(
|
16
|
+
logger.warning(
|
17
|
+
"未填写本地服务器路径,如果想要使用本地服务器功能,请填写本地服务器路径",
|
18
|
+
)
|
17
19
|
vpk_path = ""
|
18
20
|
|
19
21
|
local_path_list = config.l4_local
|
@@ -2,16 +2,19 @@ import random
|
|
2
2
|
from typing import Dict, List, Optional, cast
|
3
3
|
|
4
4
|
from nonebot.log import logger
|
5
|
+
|
5
6
|
from ..config import server_all_path
|
6
7
|
from ..l4_image import msg_to_image
|
7
8
|
from ..utils.api.models import AllServer, NserverOut, OutServer
|
8
|
-
from ..utils.utils import split_maohao
|
9
|
-
from .draw_msg import draw_one_ip, get_much_server, convert_duration
|
10
9
|
from ..utils.api.request import L4API
|
10
|
+
from ..utils.utils import split_maohao
|
11
|
+
from .draw_msg import convert_duration, draw_one_ip, get_much_server
|
12
|
+
|
11
13
|
try:
|
12
14
|
import ujson as json
|
13
15
|
except ImportError:
|
14
16
|
import json
|
17
|
+
|
15
18
|
from ..config import config
|
16
19
|
|
17
20
|
# 获取全部服务器信息
|
@@ -20,6 +23,16 @@ COMMAND = set()
|
|
20
23
|
|
21
24
|
|
22
25
|
async def get_all_server_detail():
|
26
|
+
"""
|
27
|
+
获取所有服务器的详细信息。
|
28
|
+
|
29
|
+
Args:
|
30
|
+
无
|
31
|
+
|
32
|
+
Returns:
|
33
|
+
str: 包含所有服务器详细信息的字符串。
|
34
|
+
|
35
|
+
"""
|
23
36
|
out_list: List[AllServer] = []
|
24
37
|
for group in ALLHOST:
|
25
38
|
msg_list = await get_group_detail(group)
|
@@ -47,16 +60,38 @@ async def get_all_server_detail():
|
|
47
60
|
# to do作图,先用文字凑合
|
48
61
|
out_msg = ""
|
49
62
|
for one in out_list:
|
50
|
-
|
63
|
+
if one["max_player"]:
|
64
|
+
out_msg += f"{one['command']} | 服务器{one['active_server']}/{one['max_server']} | 玩家{one['active_player']}/{one['max_player']}\n"
|
65
|
+
else:
|
66
|
+
continue
|
51
67
|
return out_msg
|
52
68
|
|
53
69
|
|
54
70
|
async def get_server_detail(
|
55
|
-
command: str,
|
71
|
+
command: str = "",
|
56
72
|
_id: Optional[str] = None,
|
57
73
|
is_img: bool = True,
|
58
74
|
):
|
59
|
-
|
75
|
+
"""
|
76
|
+
异步获取服务器详细信息。
|
77
|
+
|
78
|
+
Args:
|
79
|
+
command (str): 服务器组名。
|
80
|
+
_id (Optional[str], optional): 服务器ID。默认为None。
|
81
|
+
is_img (bool, optional): 是否返回图片格式的信息。默认为True。
|
82
|
+
|
83
|
+
Returns:
|
84
|
+
Union[bytes, List[OutServer], None]: 返回服务器详细信息。如果为图片格式,返回bytes类型;
|
85
|
+
如果不是图片格式,返回List[OutServer]类型;如果未找到服务器组,返回None。
|
86
|
+
|
87
|
+
"""
|
88
|
+
if command:
|
89
|
+
server_json = ALLHOST.get(command)
|
90
|
+
else:
|
91
|
+
server_json = []
|
92
|
+
for servers in ALLHOST.values():
|
93
|
+
server_json.extend(servers)
|
94
|
+
|
60
95
|
logger.info(server_json)
|
61
96
|
if server_json is None:
|
62
97
|
logger.warning("未找到这个组")
|
@@ -110,100 +145,139 @@ def reload_ip():
|
|
110
145
|
# print("正在读取json文件")
|
111
146
|
group_ip = []
|
112
147
|
for item in server_all_path.iterdir():
|
113
|
-
if item.is_file():
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
if one_ip.get("
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
one_ip
|
128
|
-
|
129
|
-
|
148
|
+
if item.is_file() and item.name.endswith("json"):
|
149
|
+
json_data = json.loads(item.read_text(encoding="utf-8"))
|
150
|
+
group_server = cast(Dict[str, List[NserverOut]], json_data)
|
151
|
+
|
152
|
+
for group, group_ip in group_server.items():
|
153
|
+
# 处理ip,host,port关系
|
154
|
+
for one_ip in group_ip:
|
155
|
+
if one_ip.get("ip"):
|
156
|
+
if one_ip.get("host") and one_ip.get("port"):
|
157
|
+
pass
|
158
|
+
if one_ip.get("host") and not one_ip.get("port"):
|
159
|
+
one_ip["port"] = 20715
|
160
|
+
if not one_ip.get("host"):
|
161
|
+
one_ip["host"], one_ip["port"] = split_maohao(
|
162
|
+
one_ip.get("ip"),
|
163
|
+
)
|
164
|
+
else:
|
165
|
+
if one_ip.get("host") and one_ip.get("port"):
|
166
|
+
one_ip["ip"] = f'{one_ip["host"]}:{one_ip["port"]}'
|
167
|
+
if one_ip.get("host") and not one_ip.get("port"):
|
168
|
+
one_ip["ip"] = f'{one_ip["host"]}:20715'
|
130
169
|
else:
|
131
|
-
|
132
|
-
one_ip["ip"] = f'{one_ip["host"]}:{one_ip["port"]}'
|
133
|
-
if one_ip.get("host") and not one_ip.get("port"):
|
134
|
-
one_ip["ip"] = f'{one_ip["host"]}:20715'
|
135
|
-
else:
|
136
|
-
logger.warning(f"{one_ip} 没有ip")
|
170
|
+
logger.warning(f"{one_ip} 没有ip")
|
137
171
|
|
138
|
-
|
139
|
-
|
140
|
-
|
172
|
+
ALLHOST.update({group: group_ip})
|
173
|
+
COMMAND.add(group)
|
174
|
+
logger.success(f"成功加载 {item.name.split('.')[0]} {len(group_ip)}个")
|
141
175
|
|
142
|
-
if item.name.endswith("txt"):
|
143
|
-
"""to do"""
|
144
176
|
|
145
|
-
async def tj_request(command: str = "云",tj
|
177
|
+
async def tj_request(command: str = "云", tj="tj"):
|
178
|
+
map_type = "普通药役"
|
146
179
|
server_json = ALLHOST.get(command)
|
147
180
|
logger.info(server_json)
|
148
181
|
if server_json is None:
|
149
182
|
logger.warning("未找到这个组")
|
150
|
-
return None
|
151
|
-
|
152
|
-
logger.info("
|
183
|
+
return None
|
184
|
+
|
185
|
+
logger.info("正在获取电信服务器信息")
|
153
186
|
player_msg = ""
|
154
187
|
right_ip = []
|
155
|
-
|
156
|
-
|
188
|
+
|
189
|
+
async def _filter_servers(servers: list, tj_mode: str) -> list:
|
190
|
+
"""筛选符合条件的服务器
|
191
|
+
Args:
|
192
|
+
servers: 服务器列表
|
193
|
+
tj_mode: 筛选模式('tj'或'zl')
|
194
|
+
Returns:
|
195
|
+
符合条件的服务器列表
|
196
|
+
"""
|
197
|
+
filtered = []
|
198
|
+
for i in servers:
|
199
|
+
ser_list = await L4API.a2s_info([(i["host"], i["port"])], is_player=True)
|
200
|
+
if not ser_list:
|
201
|
+
continue
|
202
|
+
|
203
|
+
srv = ser_list[0][0]
|
204
|
+
players = ser_list[0][1]
|
205
|
+
|
206
|
+
if tj_mode == "tj" and map_type in srv.map_name:
|
207
|
+
score = sum(p.score for p in players[:4])
|
208
|
+
t = srv.map_name.split("[")[-1].split("特")[0]
|
209
|
+
if t.isdigit() and int(t) * 50 < score:
|
210
|
+
logger.info(
|
211
|
+
f"符合TJ条件的服务器: {i['host']}:{i['port']}, 地图: {srv.map_name}, 分数: {score}",
|
212
|
+
)
|
213
|
+
filtered.append(i)
|
214
|
+
elif tj_mode == "zl" and map_type in srv.map_name and len(players) <= 4:
|
215
|
+
logger.info(
|
216
|
+
f"符合ZL条件的服务器: {i['host']}:{i['port']}, 地图: {srv.map_name}, 玩家数: {len(players)}",
|
217
|
+
)
|
218
|
+
filtered.append(i)
|
219
|
+
return filtered
|
220
|
+
|
221
|
+
async def _format_players(player_list: list) -> str:
|
222
|
+
"""格式化玩家信息
|
223
|
+
Args:
|
224
|
+
player_list: 玩家对象列表
|
225
|
+
Returns:
|
226
|
+
格式化后的玩家信息字符串
|
227
|
+
"""
|
228
|
+
durations = [await convert_duration(p.duration) for p in player_list]
|
229
|
+
max_duration_len = max(len(str(d)) for d in durations)
|
230
|
+
max_score_len = max(len(str(p.score)) for p in player_list)
|
231
|
+
return "\n".join(
|
232
|
+
f"[{p.score:>{max_score_len}}] | {durations[i]:^{max_duration_len}} | {p.name[0]}***{p.name[-1]}"
|
233
|
+
for i, p in enumerate(player_list)
|
234
|
+
)
|
235
|
+
|
236
|
+
def _build_message(srv_info, players_msg: str, selected_srv: dict) -> str:
|
237
|
+
"""构建服务器信息消息
|
238
|
+
Args:
|
239
|
+
srv_info: 服务器信息对象
|
240
|
+
players_msg: 格式化后的玩家信息
|
241
|
+
selected_srv: 选中的服务器信息
|
242
|
+
Returns:
|
243
|
+
完整的消息字符串
|
244
|
+
"""
|
245
|
+
msg = f"""*{srv_info.server_name}*
|
246
|
+
游戏: {srv_info.folder}
|
247
|
+
地图: {srv_info.map_name}
|
248
|
+
人数: {srv_info.player_count}/{srv_info.max_players}"""
|
249
|
+
if srv_info.ping is not None:
|
250
|
+
msg += f"\nping: {srv_info.ping * 1000:.0f}ms\n{players_msg}"
|
251
|
+
if config.l4_show_ip:
|
252
|
+
msg += f"\nconnect {selected_srv['host']}:{selected_srv['port']}"
|
253
|
+
return msg
|
254
|
+
|
255
|
+
try:
|
256
|
+
right_ip = await _filter_servers(server_json, tj)
|
257
|
+
|
258
|
+
if not right_ip:
|
259
|
+
logger.warning("没有找到符合条件的服务器")
|
260
|
+
return "没有符合条件的服务器"
|
261
|
+
|
262
|
+
logger.info(
|
263
|
+
f"符合条件的服务器列表: {[f'{ip['host']}:{ip['port']}' for ip in right_ip]}",
|
264
|
+
)
|
265
|
+
s = random.choice(right_ip)
|
266
|
+
logger.info(f"最终选择的服务器: {s['host']}:{s['port']}")
|
267
|
+
ser_list = await L4API.a2s_info([(s["host"], s["port"])], is_player=True)
|
268
|
+
if not ser_list:
|
269
|
+
return "获取服务器信息失败"
|
270
|
+
|
157
271
|
one_server = ser_list[0][0]
|
158
272
|
one_player = ser_list[0][1]
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
if int(t)*50 < score:
|
171
|
-
right_ip.append(i)
|
172
|
-
if tj == "zl":
|
173
|
-
if "普通药役" in one_server.map_name and len(one_player) <= 4:
|
174
|
-
right_ip.append(i)
|
175
|
-
|
176
|
-
|
177
|
-
if not right_ip:
|
178
|
-
return "没有符合条件的服务器"
|
179
|
-
|
180
|
-
s = random.choice(right_ip)
|
181
|
-
ser_list = await L4API.a2s_info([(s["host"], s["port"])], is_player=True)
|
182
|
-
one_server = ser_list[0][0]
|
183
|
-
one_player = ser_list[0][1]
|
184
|
-
if len(one_player):
|
185
|
-
max_duration_len = max(
|
186
|
-
[len(str(await convert_duration(i.duration))) for i in one_player],
|
187
|
-
)
|
188
|
-
max_score_len = max(len(str(i.score)) for i in one_player)
|
189
|
-
|
190
|
-
for player in one_player:
|
191
|
-
soc = "[{:>{}}]".format(player.score, max_score_len)
|
192
|
-
chines_dur = await convert_duration(player.duration)
|
193
|
-
dur = "{:^{}}".format(chines_dur, max_duration_len)
|
194
|
-
name = f"{player.name[0]}***{player.name[-1]}"
|
195
|
-
player_msg += f"{soc} | {dur} | {name} \n"
|
196
|
-
else:
|
197
|
-
player_msg = "服务器感觉很安静啊"
|
198
|
-
msg = f"""*{one_server.server_name}*
|
199
|
-
游戏: {one_server.folder}
|
200
|
-
地图: {one_server.map_name}
|
201
|
-
人数: {one_server.player_count}/{one_server.max_players}"""
|
202
|
-
if one_server.ping is not None:
|
203
|
-
msg += f"""
|
204
|
-
ping: {one_server.ping * 1000:.0f}ms
|
205
|
-
{player_msg}"""
|
206
|
-
if config.l4_show_ip:
|
207
|
-
msg += f"""
|
208
|
-
connect {s["host"]}:{s["port"]}"""
|
209
|
-
return msg
|
273
|
+
|
274
|
+
if one_player:
|
275
|
+
player_msg = await _format_players(one_player)
|
276
|
+
else:
|
277
|
+
player_msg = "服务器感觉很安静啊"
|
278
|
+
|
279
|
+
return _build_message(one_server, player_msg, s)
|
280
|
+
|
281
|
+
except Exception as e:
|
282
|
+
logger.error(f"tj_request error: {e}")
|
283
|
+
return "获取服务器信息时出错"
|
@@ -32,7 +32,7 @@ async def draw_one_ip(host: str, port: int):
|
|
32
32
|
name_leg = len(player.name)
|
33
33
|
if name_leg > 2:
|
34
34
|
xing = "*" * (name_leg - 2)
|
35
|
-
name = f"{player.name[0]}xing{player.name[-1]}"
|
35
|
+
name = f"{player.name[0]}{xing}{player.name[-1]}"
|
36
36
|
else:
|
37
37
|
name = player.name
|
38
38
|
player_msg += f"{soc} | {dur} | {name} \n"
|
@@ -77,6 +77,12 @@ async def get_much_server(server_json: List[NserverOut], command: str):
|
|
77
77
|
|
78
78
|
|
79
79
|
async def convert_duration(duration: float) -> str:
|
80
|
+
"""Convert duration in seconds to human-readable string format (e.g. '1h 30m 15s')
|
81
|
+
Args:
|
82
|
+
duration: Duration in seconds
|
83
|
+
Returns:
|
84
|
+
Formatted time string
|
85
|
+
"""
|
80
86
|
minutes, seconds = divmod(duration, 60)
|
81
87
|
hours, minutes = divmod(minutes, 60)
|
82
88
|
time_str = ""
|
@@ -6,6 +6,7 @@ from pathlib import Path
|
|
6
6
|
from typing import Any, Dict, List, Literal, Optional, Tuple, cast
|
7
7
|
|
8
8
|
import a2s
|
9
|
+
import aiofiles
|
9
10
|
import ujson as js
|
10
11
|
import ujson as json
|
11
12
|
from bs4 import BeautifulSoup
|
@@ -28,6 +29,8 @@ from .models import (
|
|
28
29
|
SourceBansInfo,
|
29
30
|
)
|
30
31
|
|
32
|
+
config_path = Path(config.l4_path) / "config.json"
|
33
|
+
|
31
34
|
|
32
35
|
class L4D2Api:
|
33
36
|
ssl_verify = False
|
@@ -76,11 +79,11 @@ class L4D2Api:
|
|
76
79
|
is_server: bool,
|
77
80
|
is_player: bool,
|
78
81
|
):
|
79
|
-
server: a2s.SourceInfo = a2s.SourceInfo()
|
80
82
|
play: List[a2s.Player] = []
|
81
83
|
if is_server:
|
82
84
|
try:
|
83
|
-
server = await a2s.ainfo(ip)
|
85
|
+
server: a2s.SourceInfo = await a2s.ainfo(ip, timeout=3, encoding="utf8")
|
86
|
+
|
84
87
|
if server is not None:
|
85
88
|
server.steam_id = index
|
86
89
|
|
@@ -89,6 +92,25 @@ class L4D2Api:
|
|
89
92
|
ConnectionRefusedError,
|
90
93
|
socket.gaierror,
|
91
94
|
):
|
95
|
+
server: a2s.SourceInfo = a2s.SourceInfo(
|
96
|
+
protocol=0,
|
97
|
+
server_name="服务器无响应",
|
98
|
+
map_name="无",
|
99
|
+
folder="m",
|
100
|
+
game="L4D2",
|
101
|
+
app_id=114514,
|
102
|
+
steam_id=index,
|
103
|
+
player_count=0,
|
104
|
+
max_players=0,
|
105
|
+
bot_count=0,
|
106
|
+
server_type="w",
|
107
|
+
platform="w",
|
108
|
+
password_protected=False,
|
109
|
+
vac_enabled=False,
|
110
|
+
version="1.0",
|
111
|
+
edf=0,
|
112
|
+
ping=0,
|
113
|
+
)
|
92
114
|
server.steam_id = index
|
93
115
|
server.player_count = 0
|
94
116
|
server.max_players = 0
|
@@ -103,7 +125,7 @@ class L4D2Api:
|
|
103
125
|
ConnectionRefusedError,
|
104
126
|
socket.gaierror,
|
105
127
|
):
|
106
|
-
play = await a2s.aplayers(ip)
|
128
|
+
play = await a2s.aplayers(ip, timeout=3, encoding="utf8")
|
107
129
|
return server, play
|
108
130
|
|
109
131
|
async def _server_request(
|
@@ -158,7 +180,7 @@ class L4D2Api:
|
|
158
180
|
html_content = resp.content
|
159
181
|
return BeautifulSoup(html_content, "lxml")
|
160
182
|
|
161
|
-
async def get_sourceban(self, tag:str = "云", url: str = anne_ban):
|
183
|
+
async def get_sourceban(self, tag: str = "云", url: str = anne_ban):
|
162
184
|
"""从sourceban++获取服务器列表,目前未做名称处理"""
|
163
185
|
if not (url.startswith(("http://", "https://"))):
|
164
186
|
url = "http://" + url # 默认添加 http://
|
@@ -184,23 +206,25 @@ class L4D2Api:
|
|
184
206
|
)
|
185
207
|
|
186
208
|
if not Path(Path(config.l4_path) / "config.json").is_file():
|
187
|
-
with
|
188
|
-
f.write("{}")
|
209
|
+
async with aiofiles.open(config_path, "w", encoding="utf-8") as f:
|
210
|
+
await f.write("{}")
|
189
211
|
with (Path(config.l4_path) / "config.json").open("r", encoding="utf-8") as f:
|
190
212
|
content = f.read().strip()
|
191
213
|
ip_json = json.loads(content)
|
192
214
|
if tag in ip_json:
|
193
215
|
url = ip_json[tag]
|
194
|
-
|
216
|
+
tag_path = Path(Path(config.l4_path) / f"l4d2/{tag}.json")
|
217
|
+
|
218
|
+
async with aiofiles.open(tag_path, "w", encoding="utf-8") as f:
|
195
219
|
print(Path(Path(config.l4_path) / f"l4d2/{tag}.json"))
|
196
220
|
up_data = {}
|
197
221
|
for server in server_list:
|
198
222
|
new_dict = {}
|
199
|
-
new_dict["id"] = int(server.index
|
223
|
+
new_dict["id"] = int(server.index) + 1
|
200
224
|
new_dict["ip"] = server.host + ":" + str(server.port)
|
201
225
|
up_data.update(new_dict)
|
202
226
|
print(up_data)
|
203
|
-
json.dump(up_data, f)
|
227
|
+
json.dump(up_data, f, ensure_ascii=False, indent=4)
|
204
228
|
return server_list
|
205
229
|
|
206
230
|
async def get_anne_steamid(self, name: str):
|
{nonebot_plugin_l4d2_server-1.0.1.dist-info → nonebot_plugin_l4d2_server-1.0.3.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: nonebot-plugin-l4d2-server
|
3
|
-
Version: 1.0.
|
3
|
+
Version: 1.0.3
|
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>
|
@@ -11,10 +11,12 @@ Classifier: Programming Language :: Python :: 3
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.9
|
12
12
|
Classifier: Programming Language :: Python :: 3.10
|
13
13
|
Classifier: Programming Language :: Python :: 3.11
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
14
15
|
Classifier: Operating System :: OS Independent
|
15
16
|
Project-URL: homepage, https://github.com/Agnes4m/nonebot_plugin_l4d2_server
|
16
17
|
Requires-Python: <4.0,>=3.9
|
17
18
|
Requires-Dist: nonebot2>=2.0.0
|
19
|
+
Requires-Dist: nonebot2[fastapi]>=2.3.3
|
18
20
|
Requires-Dist: nonebot-plugin-htmlrender>=0.3.0
|
19
21
|
Requires-Dist: nonebot-adapter-onebot>=2.4.4
|
20
22
|
Requires-Dist: nonebot-plugin-alconna>=0.50.0
|
@@ -25,12 +27,15 @@ Requires-Dist: jinja2>=3.0.0
|
|
25
27
|
Requires-Dist: srctools>=2.3.9
|
26
28
|
Requires-Dist: httpx<0.24.1,>=0.22.0
|
27
29
|
Requires-Dist: msgspec>=0.18.0
|
28
|
-
Requires-Dist: python-a2s>=1.
|
30
|
+
Requires-Dist: python-a2s>=1.4.1
|
29
31
|
Requires-Dist: ujson>=5.10.0
|
30
32
|
Requires-Dist: lxml>=5.2.2
|
31
33
|
Requires-Dist: rcon>=2.1.0
|
32
|
-
Requires-Dist: pillow>
|
34
|
+
Requires-Dist: pillow>10
|
33
35
|
Requires-Dist: beautifulsoup4>=4.12.3
|
36
|
+
Requires-Dist: rarfile>=4.2
|
37
|
+
Requires-Dist: pyunpack>=0.3
|
38
|
+
Requires-Dist: aiofiles>=24.1.0
|
34
39
|
Description-Content-Type: text/markdown
|
35
40
|
|
36
41
|
<!-- markdownlint-disable MD026 MD031 MD033 MD036 MD041 MD046 MD051 -->
|
{nonebot_plugin_l4d2_server-1.0.1.dist-info → nonebot_plugin_l4d2_server-1.0.3.dist-info}/RECORD
RENAMED
@@ -1,15 +1,15 @@
|
|
1
|
-
nonebot_plugin_l4d2_server-1.0.
|
2
|
-
nonebot_plugin_l4d2_server-1.0.
|
3
|
-
nonebot_plugin_l4d2_server-1.0.
|
4
|
-
nonebot_plugin_l4d2_server-1.0.
|
1
|
+
nonebot_plugin_l4d2_server-1.0.3.dist-info/METADATA,sha256=po5LeZo-Nk7oQNuaxa5Ve_3AANnFc4RxG8fnBbcqeK0,6688
|
2
|
+
nonebot_plugin_l4d2_server-1.0.3.dist-info/WHEEL,sha256=tSfRZzRHthuv7vxpI4aehrdN9scLjk-dCJkPLzkHxGg,90
|
3
|
+
nonebot_plugin_l4d2_server-1.0.3.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
|
4
|
+
nonebot_plugin_l4d2_server-1.0.3.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
5
5
|
nonebot_plugin_l4d2_server/__init__.py,sha256=PIufOk1bxOx-nYqdoinoB7BcntIETNFwfzqKnmu-teE,1624
|
6
|
-
nonebot_plugin_l4d2_server/__main__.py,sha256=
|
7
|
-
nonebot_plugin_l4d2_server/config.py,sha256=
|
6
|
+
nonebot_plugin_l4d2_server/__main__.py,sha256=AGqPjt0O6dW_E3D93hcqehtsPsvzSRC1qEtx6HqEAGo,8455
|
7
|
+
nonebot_plugin_l4d2_server/config.py,sha256=ZLYJr9lVs1Q5en1m4EA0wF7H366EVm8-6AnN8dcyy7c,1394
|
8
8
|
nonebot_plugin_l4d2_server/data/font/loli.ttf,sha256=Yrh-RPoCrn1-NG94DR0x20ASXYUt8g3Ep6BCt3CdOFk,11125812
|
9
9
|
nonebot_plugin_l4d2_server/l4_anne/__init__.py,sha256=zX4kTu4LAsqm7_li0-GcihnbYI65SBPkfUCPTs79U8k,4124
|
10
10
|
nonebot_plugin_l4d2_server/l4_anne/ranne.py,sha256=vtNQJ-74rJiwVI3IYS3ZgFk_LaCviKU1yxrGk3DFvQs,532
|
11
11
|
nonebot_plugin_l4d2_server/l4_help/Help.json,sha256=hz_k-qmGmGJKwCa8wmkHTLzXu6C-G-E_g7xdK-t6fNk,2673
|
12
|
-
nonebot_plugin_l4d2_server/l4_help/__init__.py,sha256=
|
12
|
+
nonebot_plugin_l4d2_server/l4_help/__init__.py,sha256=YNR3eob0XvRb_-4wIP9ZcSMey0RuFS4RlHMZ217Bn0E,1535
|
13
13
|
nonebot_plugin_l4d2_server/l4_help/draw.py,sha256=y6yDPUnoZFvwly8cf7g9HRpT1JXTxyA9DC1TuvUinTM,6448
|
14
14
|
nonebot_plugin_l4d2_server/l4_help/icon/介绍.png,sha256=3QC6A38QC-7gDBxmtQSQdbsz7hsefU5LL-oZmJ41zTk,3806
|
15
15
|
nonebot_plugin_l4d2_server/l4_help/icon/任务.png,sha256=nyZ4_kM21ZO95nwanCFnUfCGX-PkmJXYQf9OrJVKomY,3782
|
@@ -86,14 +86,14 @@ nonebot_plugin_l4d2_server/l4_image/img/template/vue.css,sha256=2sGjCFrR-3tFMB_x
|
|
86
86
|
nonebot_plugin_l4d2_server/l4_image/img/template/w.svg,sha256=LnctC2mVwjdhRMiS9ffrjUX-9KGwqb6OMmpJXvVXl70,486
|
87
87
|
nonebot_plugin_l4d2_server/l4_image/model.py,sha256=FGsCvf_BKbRNJUVy6I5BKnArMY-3JEIdqeX2gs93E5o,231
|
88
88
|
nonebot_plugin_l4d2_server/l4_image/vtfs.py,sha256=He_7zzEIOip8MXP55TS7aWPbzo6ac0wPf602nN3GWZM,1461
|
89
|
-
nonebot_plugin_l4d2_server/l4_local/__init__.py,sha256=
|
89
|
+
nonebot_plugin_l4d2_server/l4_local/__init__.py,sha256=nz9KUxmuvRrryHakQQ4B30oC9jeGWZ3sON1tCKb-lS0,3603
|
90
90
|
nonebot_plugin_l4d2_server/l4_local/file.py,sha256=hew1Y8kV3uSZvUGplmi09EGKC89-sUJWsWV7SCEstI8,3067
|
91
|
-
nonebot_plugin_l4d2_server/l4_request/__init__.py,sha256=
|
92
|
-
nonebot_plugin_l4d2_server/l4_request/draw_msg.py,sha256=
|
91
|
+
nonebot_plugin_l4d2_server/l4_request/__init__.py,sha256=ukb90pROfHtLbIRSya5pRAGSpzB9Bb5gPi3kAH2-41Y,9588
|
92
|
+
nonebot_plugin_l4d2_server/l4_request/draw_msg.py,sha256=efmlEBqbbOozf50InPNqONGIZ0D2hI4xAGPDROIcHBY,2883
|
93
93
|
nonebot_plugin_l4d2_server/l4_request/utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
94
94
|
nonebot_plugin_l4d2_server/utils/api/api.py,sha256=auvDicCEKwvnm6EJYeCxCtugFby61K-zAmmaRWWEwtM,296
|
95
95
|
nonebot_plugin_l4d2_server/utils/api/models.py,sha256=EKAAM5RsFK-hV-a80tc4r35ToNRhnCWVuQ-7imwBjwE,2386
|
96
|
-
nonebot_plugin_l4d2_server/utils/api/request.py,sha256=
|
96
|
+
nonebot_plugin_l4d2_server/utils/api/request.py,sha256=hugBaPBK-gUr5Xemyk7dSHP1JlzYrEZcXPinkNX0AuU,14987
|
97
97
|
nonebot_plugin_l4d2_server/utils/database/models.py,sha256=SLdcgwsn39r_ZkcBoqf4MLX1EfpCOjGBwWcR16u9Bqo,454
|
98
98
|
nonebot_plugin_l4d2_server/utils/utils.py,sha256=C-QzwtUCbq7QXmHplpmo3OTKbRztE1W7V-IywvufDl0,5971
|
99
|
-
nonebot_plugin_l4d2_server-1.0.
|
99
|
+
nonebot_plugin_l4d2_server-1.0.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|