nonebot-plugin-l4d2-server 0.3.4a2__py3-none-any.whl → 0.3.5__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.
Files changed (32) hide show
  1. README.md +23 -3
  2. nonebot_plugin_l4d2_server/__init__.py +75 -20
  3. nonebot_plugin_l4d2_server/command.py +5 -4
  4. nonebot_plugin_l4d2_server/config.py +26 -11
  5. nonebot_plugin_l4d2_server/data/L4D2/image/template/anne.html +7 -4
  6. nonebot_plugin_l4d2_server/data/L4D2/image/template/back.png +0 -0
  7. nonebot_plugin_l4d2_server/data/L4D2/image/template/help - /321/205/320/231/320/277/321/206/320/254/320/274.html" +232 -0
  8. nonebot_plugin_l4d2_server/data/L4D2/image/template/help.html +1 -1
  9. nonebot_plugin_l4d2_server/data/L4D2/image/template/help_dack.html +231 -0
  10. nonebot_plugin_l4d2_server/data/L4D2/image/template/ip.html +1 -1
  11. nonebot_plugin_l4d2_server/data/L4D2/l4d2.json +27 -139
  12. nonebot_plugin_l4d2_server/l4d2_anne/__init__.py +40 -17
  13. nonebot_plugin_l4d2_server/l4d2_anne/analysis.py +40 -0
  14. nonebot_plugin_l4d2_server/l4d2_anne/anne_telecom.py +70 -70
  15. nonebot_plugin_l4d2_server/l4d2_anne/server.py +2 -1
  16. nonebot_plugin_l4d2_server/l4d2_anne/startand.py +18 -0
  17. nonebot_plugin_l4d2_server/l4d2_file/__init__.py +28 -2
  18. nonebot_plugin_l4d2_server/l4d2_image/__init__.py +8 -11
  19. nonebot_plugin_l4d2_server/l4d2_queries/__init__.py +4 -0
  20. nonebot_plugin_l4d2_server/l4d2_queries/maps.py +27 -0
  21. nonebot_plugin_l4d2_server/l4d2_queries/qqgroup.py +12 -7
  22. nonebot_plugin_l4d2_server/l4d2_server/web.py +221 -0
  23. nonebot_plugin_l4d2_server/l4d2_server/webUI.py +353 -0
  24. nonebot_plugin_l4d2_server/l4d2_server/workshop.py +24 -5
  25. nonebot_plugin_l4d2_server/seach.py +2 -1
  26. nonebot_plugin_l4d2_server/utils.py +5 -7
  27. {nonebot_plugin_l4d2_server-0.3.4a2.dist-info → nonebot_plugin_l4d2_server-0.3.5.dist-info}/METADATA +28 -4
  28. nonebot_plugin_l4d2_server-0.3.5.dist-info/RECORD +49 -0
  29. nonebot_plugin_l4d2_server/data/L4D2/image/template/back2.jpg +0 -0
  30. nonebot_plugin_l4d2_server-0.3.4a2.dist-info/RECORD +0 -43
  31. {nonebot_plugin_l4d2_server-0.3.4a2.dist-info → nonebot_plugin_l4d2_server-0.3.5.dist-info}/LICENSE +0 -0
  32. {nonebot_plugin_l4d2_server-0.3.4a2.dist-info → nonebot_plugin_l4d2_server-0.3.5.dist-info}/WHEEL +0 -0
@@ -1,76 +1,76 @@
1
- from ..l4d2_image.steam import url_to_byte
2
- from bs4 import BeautifulSoup
3
- from typing import List
1
+ # from ..l4d2_image.steam import url_to_byte
2
+ # from bs4 import BeautifulSoup
3
+ # from typing import List
4
4
 
5
- # 计划获取电信服ip
6
- class ANNE_API:
5
+ # 暂时废弃
6
+ # class ANNE_API:
7
7
 
8
- async def __init__(
9
- self,
10
- STEAMID:str,
11
- tag:str
12
- ):
13
- self.STEAMID = STEAMID
14
- if tag == '中':
15
- msg1 = await self.anne_msg()
16
- elif tag == '长':
17
- msg1 = await self.anne_msg()
18
- msg1.update(await self.anne_map())
8
+ # async def __init__(
9
+ # self,
10
+ # STEAMID:str,
11
+ # tag:str
12
+ # ):
13
+ # self.STEAMID = STEAMID
14
+ # if tag == '中':
15
+ # msg1 = await self.anne_msg()
16
+ # elif tag == '长':
17
+ # msg1 = await self.anne_msg()
18
+ # msg1.update(await self.anne_map())
19
19
 
20
20
 
21
- async def anne_msg(self):
22
- """个人资料表"""
23
- data_bytes = await url_to_byte('https://sb.trygek.com/l4d_stats/ranking/player.php?steamid={self.STEAMID}')
24
- data_bs = BeautifulSoup(data_bytes, 'html.parser')
25
- data_fom = data_bs.find_all('table')
26
- n = 0
27
- data_dict = {}
28
- while n < 2:
29
- data_list:List[dict] = []
30
- detail2 = data_fom[n]
31
- tr = detail2.find_all('tr')
32
- for i in tr:
33
- title = i.find('td', {'class': 'w-50'})
34
- value = title.find_next_sibling('td')
35
- new_dict = {title.text:value.text}
36
- data_dict.update(new_dict)
37
- data_list.append(data_dict)
38
- n += 1
39
- # 获取头像
40
- element:str = data_fom.find_all(attrs={"style": "cursor:pointer"})[0].get("onclick")
41
- player_url = element.split("'")[1]
42
- data_list[0].update({"个人资料":player_url})
43
- # 获取一言
44
- message = data_fom.select("html body div.content.text-center.text-md-left div.container.text-left div.col-md-12.h-100 div.card-body.worldmap.d-flex.flex-column.justify-content-center.text-center span")
45
- msg_list = []
46
- for i in message:
47
- msg_list.append(i.text)
48
- data_list[0].update({"一言":msg_list})
49
- return data_list
21
+ # async def anne_msg(self):
22
+ # """个人资料表"""
23
+ # data_bytes = await url_to_byte('https://sb.trygek.com/l4d_stats/ranking/player.php?steamid={self.STEAMID}')
24
+ # data_bs = BeautifulSoup(data_bytes, 'html.parser')
25
+ # data_fom = data_bs.find_all('table')
26
+ # n = 0
27
+ # data_dict = {}
28
+ # while n < 2:
29
+ # data_list:List[dict] = []
30
+ # detail2 = data_fom[n]
31
+ # tr = detail2.find_all('tr')
32
+ # for i in tr:
33
+ # title = i.find('td', {'class': 'w-50'})
34
+ # value = title.find_next_sibling('td')
35
+ # new_dict = {title.text:value.text}
36
+ # data_dict.update(new_dict)
37
+ # data_list.append(data_dict)
38
+ # n += 1
39
+ # # 获取头像
40
+ # element:str = data_fom.find_all(attrs={"style": "cursor:pointer"})[0].get("onclick")
41
+ # player_url = element.split("'")[1]
42
+ # data_list[0].update({"个人资料":player_url})
43
+ # # 获取一言
44
+ # message = data_fom.select("html body div.content.text-center.text-md-left div.container.text-left div.col-md-12.h-100 div.card-body.worldmap.d-flex.flex-column.justify-content-center.text-center span")
45
+ # msg_list = []
46
+ # for i in message:
47
+ # msg_list.append(i.text)
48
+ # data_list[0].update({"一言":msg_list})
49
+ # return data_list
50
50
 
51
- async def anne_map(self):
52
- """个人地图表"""
53
- data_dict = {}
54
- data_bytes = await url_to_byte('https://sb.trygek.com/l4d_stats/ranking/timedmaps.php?steamid={self.STEAMID}')
55
- data_bs = BeautifulSoup(data_bytes, 'html.parser')
56
- tbody = data_bs.select('tbody')
57
- for tr in tbody:
58
- tds = tr.select('td')
59
- n = 0
60
- for td in tds:
61
- n += 1
62
- title:str = td['data-title'][:-1]
63
- data_text = td.text
64
- if title == '特感数量':
65
- special_amount = data_text
66
- elif title == '刷新间隔':
67
- refresh_interval = data_text
68
- else:
69
- if title in data_dict:
70
- data_dict[title].append(data_text)
71
- else:
72
- data_dict[title] = [data_text]
73
- if special_amount and refresh_interval:
74
- data_dict['刷特时间'] = special_amount + refresh_interval
51
+ # async def anne_map(self):
52
+ # """个人地图表"""
53
+ # data_dict = {}
54
+ # data_bytes = await url_to_byte('https://sb.trygek.com/l4d_stats/ranking/timedmaps.php?steamid={self.STEAMID}')
55
+ # data_bs = BeautifulSoup(data_bytes, 'html.parser')
56
+ # tbody = data_bs.select('tbody')
57
+ # for tr in tbody:
58
+ # tds = tr.select('td')
59
+ # n = 0
60
+ # for td in tds:
61
+ # n += 1
62
+ # title:str = td['data-title'][:-1]
63
+ # data_text = td.text
64
+ # if title == '特感数量':
65
+ # special_amount = data_text
66
+ # elif title == '刷新间隔':
67
+ # refresh_interval = data_text
68
+ # else:
69
+ # if title in data_dict:
70
+ # data_dict[title].append(data_text)
71
+ # else:
72
+ # data_dict[title] = [data_text]
73
+ # if special_amount and refresh_interval:
74
+ # data_dict['刷特时间'] = special_amount + refresh_interval
75
75
 
76
- return data_dict
76
+ # return data_dict
@@ -60,4 +60,5 @@ def server_key():
60
60
  except AttributeError:
61
61
  a.add('希腊那我从来没有想过这个事情')
62
62
  return a
63
-
63
+
64
+
@@ -0,0 +1,18 @@
1
+
2
+ NUMBER_MAP = [4,5,4,5,5,3,3,5,2,5,5,5,4,2]
3
+ SAVE_MAP = [
4
+ "c1m4_atrium",
5
+ "c2m5_concert",
6
+ "c3m4_plantation",
7
+ "c4m5_milltown_escape",
8
+ "c5m5_bridge",
9
+ "c6m3_port",
10
+ "c7m3_port",
11
+ "c8m5_rooftop",
12
+ "c9m2_lots",
13
+ "c10m5_houseboat",
14
+ "c11m5_runway",
15
+ "c12m5_cornfield",
16
+ "c13m4_cutthroatcreek",
17
+ "c14m2_lighthouse"
18
+ ]
@@ -3,6 +3,8 @@ from zipfile import ZipFile
3
3
  from time import sleep
4
4
  import sys
5
5
  import os
6
+ import io
7
+ from typing import List
6
8
 
7
9
  from ..utils import get_file,get_vpk
8
10
  from ..config import systems
@@ -39,7 +41,7 @@ def open_packet(name:str,down_file:Path):
39
41
  if systems == 'win':
40
42
  if name.endswith('.zip'):
41
43
  mes = 'zip文件已下载,正在解压'
42
- with ZipFile(down_file, 'r') as z:
44
+ with support_gbk(ZipFile(down_file, 'r')) as z:
43
45
  z.extractall(down_path)
44
46
  os.remove(down_file)
45
47
  elif name.endswith('.7z'):
@@ -99,4 +101,28 @@ def support_gbk(zip_file):
99
101
  del name_to_info[name]
100
102
  name_to_info[real_name] = info
101
103
  zip_file = rar_file
102
- return zip_file
104
+ return zip_file
105
+
106
+
107
+
108
+
109
+ async def all_zip_to_one(data_list:List[bytes]):
110
+ """多压缩包文件合并"""
111
+ file_list = []
112
+ for data in data_list:
113
+ # 将每个bytes对象解压缩成文件对象
114
+ # 将文件对象存储在一个列表中
115
+ file_list.append(io.BytesIO(data))
116
+
117
+ # 创建一个新的BytesIO对象
118
+ data_file = io.BytesIO()
119
+
120
+ # 使用zipfile将列表中的文件对象添加到zipfile中
121
+ with ZipFile(data_file, mode='w') as zf:
122
+ for i, file in enumerate(file_list):
123
+ # 将文件名设置为"file{i}.zip",i为文件在列表中的索引
124
+ filename = f"file{i}.zip"
125
+ zf.writestr(filename, file.getvalue())
126
+
127
+ # 获取zipfile的bytes对象
128
+ return data_file.getvalue()
@@ -4,11 +4,12 @@ from nonebot.log import logger
4
4
  from nonebot_plugin_htmlrender import html_to_pic
5
5
  from typing import List,Optional
6
6
  # from .htmlimg import dict_to_dict_img
7
- from ..l4d2_anne.anne_telecom import ANNE_API
7
+ # from ..l4d2_anne.anne_telecom import ANNE_API
8
8
  from ..config import TEXT_PATH
9
9
  from .download import get_head_by_user_id_and_save
10
10
  from .send_image_tool import convert_img
11
11
  import jinja2
12
+ from ..config import l4_style
12
13
  template_path = TEXT_PATH/"template"
13
14
 
14
15
  env = jinja2.Environment(
@@ -50,6 +51,7 @@ async def dict_to_html(usr_id,DETAIL_MAP:dict,soup:BeautifulSoup):
50
51
  DETAIL_right['playtimes'] = DETAIL_MAP['游玩地图数量:']
51
52
  DETAIL_right['url'] = DETAIL_MAP['个人资料']
52
53
  DETAIL_right['one_msg'] = DETAIL_MAP['一言']
54
+ DETAIL_right['last_one'] = DETAIL_MAP['救援关']
53
55
  # html_text = soup.prettify()
54
56
  # for key, value in DETAIL_right.items():
55
57
  # html_text = html_text.replace(key,value)
@@ -80,22 +82,17 @@ async def server_ip_pic(msg_list:List[dict]):
80
82
  server_info['Players'] = players_list
81
83
  print(server_info['Players'])
82
84
 
83
- # 返回更新后的 msg_list
84
- # template_path = TEXT_PATH/"template"
85
- # template = env.get_template('ip.html')
86
85
  pic = await get_help_img(msg_list)
87
- # html = await template.render_async(data = msg_list)
88
- # pic = await html_to_pic(
89
- # html,
90
- # wait=0,
91
- # viewport={"width": 1080, "height": 400},
92
- # template_path=f"file://{template_path.absolute()}",)
86
+
93
87
  return pic
94
88
 
95
89
 
96
90
  async def get_help_img(plugins: List[dict]) -> Optional[bytes]:
97
91
  try:
98
- template = env.get_template("help.html")
92
+ if l4_style == 'black':
93
+ template = env.get_template("help_dack.html")
94
+ else:
95
+ template = env.get_template("help.html")
99
96
  content = await template.render_async(plugins=plugins)
100
97
  return await html_to_pic(
101
98
  content,
@@ -7,6 +7,7 @@ async def queries(ip:str,port:int):
7
7
  message = 'ip:' + msg_dict['ip'] + '\n'
8
8
  message += '名称:' + msg_dict['name'] + '\n'
9
9
  message += f"地图:{msg_dict['map_']}\n"
10
+ message += f"延迟:{msg_dict['ping']}\n"
10
11
  message += f"玩家:{msg_dict['players']} / {msg_dict['max_players']}\n"
11
12
  return message
12
13
 
@@ -23,10 +24,12 @@ async def queries_dict(ip:str,port:int) -> dict:
23
24
  msg_dict['players'] = msg.player_count
24
25
  msg_dict['max_players'] = msg.max_players
25
26
  msg_dict['ip'] = str(ip) + ':' +str(port)
27
+ msg_dict['ping'] = f"{msg.ping*1000:.0f}ms"
26
28
  if msg_dict['players'] < msg_dict['max_players']:
27
29
  msg_dict['enabled'] = True
28
30
  else:
29
31
  msg_dict['enabled'] = False
32
+ print(msg_dict)
30
33
  return msg_dict
31
34
 
32
35
  async def player_queries_anne_dict(ip:str,port:int):
@@ -73,6 +76,7 @@ async def msg_ip_to_list(message_list:list):
73
76
  max_duration_len = max([len(str(i['Duration'])) for i in message_list])
74
77
  max_score_len = max([len(str(i['Score'])) for i in message_list])
75
78
  for i in message_list:
79
+ print(i)
76
80
  n += 1
77
81
  name = i['name']
78
82
  Score = i['Score']
@@ -0,0 +1,27 @@
1
+ import httpx
2
+
3
+
4
+ async def seach_map(msg:str,qq:str,key:str):
5
+ url = "http://106.13.207.45:4015/l4d2"
6
+ json = {
7
+ "mode":"zh",
8
+ "map_name":msg,
9
+ "qq":qq,
10
+ "key":key
11
+ }
12
+ print(json)
13
+ file = httpx.post(url,json=json)
14
+ if file.status_code == 200:
15
+ return file.json()
16
+ elif file.status_code == 204:
17
+ return "没有结果"
18
+ elif file.status_code == 406:
19
+ return "参数错误"
20
+ elif file.status_code == 401:
21
+ return file.json()
22
+
23
+ async def map_dict_to_str(data:dict):
24
+ msg = ""
25
+ for key,value in data[0].items():
26
+ msg += f"{key}:{value}\n"
27
+ return msg
@@ -54,6 +54,7 @@ async def qq_ip_queries(msg:List[tuple]):
54
54
  async def qq_ip_queries_pic(msg:list):
55
55
  """输入一个ip的三元元组组成的列表,返回一个输出消息的图片"""
56
56
  msg_list = []
57
+ pic = None
57
58
  if msg != []:
58
59
  for i in msg:
59
60
  try:
@@ -71,6 +72,8 @@ async def qq_ip_queries_pic(msg:list):
71
72
  except errors:
72
73
  continue
73
74
  pic = await server_ip_pic(msg_list)
75
+ if pic == None:
76
+ return None
74
77
  return pic
75
78
 
76
79
  async def get_tan_jian(msg:List[tuple],mode:int):
@@ -247,10 +250,12 @@ async def write_json(data_str:str):
247
250
  return '删除成功喵'
248
251
  return '序号不正确,请输入【求生更新 删除 腐竹 序号】'
249
252
  return '腐竹名不存在,请输入【求生更新 删除 腐竹 序号】'
250
-
251
-
252
- ips = ALL_HOST['云']
253
- ip_anne_list = []
254
- for one_ip in ips:
255
- host,port = split_maohao(one_ip['ip'])
256
- ip_anne_list.append((one_ip['id'],host,port))
253
+ ip_anne_list=[]
254
+ try:
255
+ ips = ALL_HOST['云']
256
+ ip_anne_list = []
257
+ for one_ip in ips:
258
+ host,port = split_maohao(one_ip['ip'])
259
+ ip_anne_list.append((one_ip['id'],host,port))
260
+ except KeyError:
261
+ pass
@@ -0,0 +1,221 @@
1
+ import datetime
2
+ from typing import Optional, Union
3
+
4
+ from fastapi import FastAPI
5
+ from fastapi import Header, HTTPException, Depends
6
+ from fastapi.responses import JSONResponse, HTMLResponse, RedirectResponse
7
+ from jose import jwt
8
+ from nonebot import get_bot, get_app
9
+ from pydantic import BaseModel
10
+ from typing import List, Dict
11
+ from pathlib import Path
12
+
13
+ from pydantic import BaseModel, Field
14
+
15
+ from nonebot import get_driver, logger
16
+ from ruamel import yaml
17
+
18
+ CONFIG_PATH = Path() / 'data' / 'L4D2' / 'l4d2.yml'
19
+ CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
20
+
21
+ driver = get_driver()
22
+
23
+ from .webUI import login_page, admin_app
24
+
25
+ requestAdaptor = '''
26
+ requestAdaptor(api) {
27
+ api.headers["token"] = localStorage.getItem("token");
28
+ return api;
29
+ },
30
+ '''
31
+ responseAdaptor = '''
32
+ responseAdaptor(api, payload, query, request, response) {
33
+ if (response.data.detail == '登录验证失败或已失效,请重新登录') {
34
+ window.location.href = '/l4d2/login'
35
+ window.localStorage.clear()
36
+ window.sessionStorage.clear()
37
+ window.alert('登录验证失败或已失效,请重新登录')
38
+ }
39
+ return payload
40
+ },
41
+ '''
42
+
43
+
44
+ def authentication():
45
+ def inner(token: Optional[str] = Header(...)):
46
+ try:
47
+ payload = jwt.decode(token, config_manager.config.web_secret_key, algorithms='HS256')
48
+ if not (username := payload.get('username')) or username != config_manager.config.web_username:
49
+ raise HTTPException(status_code=400, detail='登录验证失败或已失效,请重新登录')
50
+ except (jwt.JWTError, jwt.ExpiredSignatureError, AttributeError):
51
+ raise HTTPException(status_code=400, detail='登录验证失败或已失效,请重新登录')
52
+
53
+ return Depends(inner)
54
+
55
+
56
+ COMMAND_START = driver.config.command_start.copy()
57
+ if '' in COMMAND_START:
58
+ COMMAND_START.remove('')
59
+
60
+
61
+ class ChatGroupConfig(BaseModel):
62
+ enable: bool = Field(True, alias='群聊学习开关')
63
+ ban_words: List[str] = Field([], alias='屏蔽词')
64
+ ban_users: List[int] = Field([], alias='屏蔽用户')
65
+ answer_threshold: int = Field(4, alias='回复阈值')
66
+ answer_threshold_weights: List[int] = Field([10, 30, 60], alias='回复阈值权重')
67
+ repeat_threshold: int = Field(3, alias='复读阈值')
68
+ break_probability: float = Field(0.25, alias='打断复读概率')
69
+ speak_enable: bool = Field(True, alias='主动发言开关')
70
+ speak_threshold: int = Field(5, alias='主动发言阈值')
71
+ speak_min_interval: int = Field(300, alias='主动发言最小间隔')
72
+ speak_continuously_probability: float = Field(0.5, alias='连续主动发言概率')
73
+ speak_continuously_max_len: int = Field(3, alias='最大连续主动发言句数')
74
+ speak_poke_probability: float = Field(0.5, alias='主动发言附带戳一戳概率')
75
+
76
+ def update(self, **kwargs):
77
+ for key, value in kwargs.items():
78
+ if key in self.__fields__:
79
+ self.__setattr__(key, value)
80
+
81
+
82
+ class ChatConfig(BaseModel):
83
+ total_enable: bool = Field(True, alias='群聊学习总开关')
84
+ enable_web: bool = Field(True, alias='启用后台管理')
85
+ web_username: str = Field('chat', alias='后台管理用户名')
86
+ web_password: str = Field('admin', alias='后台管理密码')
87
+ web_secret_key: str = Field('49c294d32f69b732ef6447c18379451ce1738922a75cd1d4812ef150318a2ed0',
88
+ alias='后台管理token密钥')
89
+ ban_words: List[str] = Field([], alias='全局屏蔽词')
90
+ ban_users: List[int] = Field([], alias='全局屏蔽用户')
91
+ KEYWORDS_SIZE: int = Field(3, alias='单句关键词分词数量')
92
+ cross_group_threshold: int = Field(3, alias='跨群回复阈值')
93
+ learn_max_count: int = Field(6, alias='最高学习次数')
94
+ dictionary: List[str] = Field([], alias='自定义词典')
95
+ group_config: Dict[int, ChatGroupConfig] = Field({}, alias='分群配置')
96
+
97
+ def update(self, **kwargs):
98
+ for key, value in kwargs.items():
99
+ if key in self.__fields__:
100
+ self.__setattr__(key, value)
101
+
102
+ class ChatConfigManager:
103
+
104
+ def __init__(self):
105
+ self.file_path = CONFIG_PATH
106
+ if self.file_path.exists():
107
+ self.config = ChatConfig.parse_obj(
108
+ yaml.load(self.file_path.read_text(encoding='utf-8'), Loader=yaml.Loader))
109
+ else:
110
+ self.config = ChatConfig()
111
+ self.save()
112
+
113
+ def get_group_config(self, group_id: int) -> ChatGroupConfig:
114
+ if group_id not in self.config.group_config:
115
+ self.config.group_config[group_id] = ChatGroupConfig()
116
+ self.save()
117
+ return self.config.group_config[group_id]
118
+
119
+ @property
120
+ def config_list(self) -> List[str]:
121
+ return list(self.config.dict(by_alias=True).keys())
122
+
123
+ def save(self):
124
+ with self.file_path.open('w', encoding='utf-8') as f:
125
+ yaml.dump(
126
+ self.config.dict(by_alias=True),
127
+ f,
128
+ indent=2,
129
+ Dumper=yaml.RoundTripDumper,
130
+ allow_unicode=True)
131
+
132
+ config_manager = ChatConfigManager()
133
+
134
+ class UserModel(BaseModel):
135
+ username: str
136
+ password: str
137
+
138
+
139
+ @driver.on_startup
140
+ async def init_web():
141
+ if not config_manager.config.enable_web:
142
+ return
143
+ app: FastAPI = get_app()
144
+
145
+ @app.post('/l4d2/api/login', response_class=JSONResponse)
146
+ async def login(user: UserModel):
147
+ if user.username != config_manager.config.web_username or user.password != config_manager.config.web_password:
148
+ return {
149
+ 'status': -100,
150
+ 'msg': '登录失败,请确认用户ID和密码无误'
151
+ }
152
+ token = jwt.encode({'username': user.username,
153
+ 'exp': datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(
154
+ minutes=30)}, config_manager.config.web_secret_key, algorithm='HS256')
155
+ return {
156
+ 'status': 0,
157
+ 'msg': '登录成功',
158
+ 'data': {
159
+ 'token': token
160
+ }
161
+ }
162
+
163
+ @app.get('/l4d2/api/get_group_list', response_class=JSONResponse, dependencies=[authentication()])
164
+ async def get_group_list_api():
165
+ try:
166
+ group_list = await get_bot().get_group_list()
167
+ group_list = [{'label': f'{group["group_name"]}({group["group_id"]})', 'value': group['group_id']} for group
168
+ in group_list]
169
+ return {
170
+ 'status': 0,
171
+ 'msg': 'ok',
172
+ 'data': {
173
+ 'group_list': group_list
174
+ }
175
+ }
176
+ except ValueError:
177
+ return {
178
+ 'status': -100,
179
+ 'msg': '获取群和好友列表失败,请确认已连接GOCQ'
180
+ }
181
+
182
+ @app.get('/l4d2/api/chat_global_config', response_class=JSONResponse, dependencies=[authentication()])
183
+ async def get_chat_global_config():
184
+ try:
185
+ bot = get_bot()
186
+ groups = await bot.get_group_list()
187
+ member_list = []
188
+ for group in groups:
189
+ members = await bot.get_group_member_list(group_id=group['group_id'])
190
+ member_list.extend(
191
+ [{'label': f'{member["nickname"] or member["card"]}({member["user_id"]})',
192
+ 'value': member['user_id']}
193
+ for
194
+ member in members])
195
+ config = config_manager.config.dict(exclude={'group_config'})
196
+ config['member_list'] = member_list
197
+ return config
198
+ except ValueError:
199
+ return {
200
+ 'status': -100,
201
+ 'msg': '获取群和好友列表失败,请确认已连接GOCQ'
202
+ }
203
+
204
+
205
+
206
+ @app.get('/l4d2', response_class=RedirectResponse)
207
+ async def redirect_page():
208
+ return RedirectResponse('/l4d2/login')
209
+
210
+ @app.get('/l4d2/login', response_class=HTMLResponse)
211
+ async def login_page_app():
212
+ return login_page.render(site_title='登录 | Learning-Chat 后台管理',
213
+ theme='ang')
214
+
215
+ @app.get('/l4d2/admin', response_class=HTMLResponse)
216
+ async def admin_page_app():
217
+ return admin_app.render(site_title='Learning-Chat 后台管理',
218
+ theme='ang',
219
+ requestAdaptor=requestAdaptor,
220
+ responseAdaptor=responseAdaptor)
221
+