nonebot-plugin-l4d2-server 0.3.5b1__tar.gz → 0.3.5b2__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 (46) hide show
  1. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/PKG-INFO +7 -4
  2. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/README.md +3 -3
  3. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/__init__.py +3 -0
  4. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/command.py +4 -4
  5. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/config.py +18 -13
  6. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/data/L4D2/l4d2.json +27 -7
  7. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/l4d2_queries/qqgroup.py +1 -1
  8. nonebot_plugin_l4d2_server-0.3.5b2/nonebot_plugin_l4d2_server/l4d2_server/web.py +221 -0
  9. nonebot_plugin_l4d2_server-0.3.5b2/nonebot_plugin_l4d2_server/l4d2_server/webUI.py +353 -0
  10. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/pyproject.toml +4 -2
  11. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/setup.py +5 -2
  12. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/LICENSE +0 -0
  13. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/data/L4D2/image/head/head.png +0 -0
  14. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/data/L4D2/image/header/player1.jpg +0 -0
  15. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/data/L4D2/image/template/anne.html +0 -0
  16. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/data/L4D2/image/template/back.png +0 -0
  17. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/data/L4D2/image/template/fingerprint.svg +0 -0
  18. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/data/L4D2/image/template/help - /345/211/257/346/234/254.html" +0 -0
  19. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/data/L4D2/image/template/help.html +0 -0
  20. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/data/L4D2/image/template/help_dack.html +0 -0
  21. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/data/L4D2/image/template/ip.html +0 -0
  22. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/data/L4D2/image/template/vue.css +0 -0
  23. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/l4d2_anne/__init__.py +0 -0
  24. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/l4d2_anne/anne_telecom.py +0 -0
  25. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/l4d2_anne/server.py +0 -0
  26. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/l4d2_data/__init__.py +0 -0
  27. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/l4d2_data/config.py +0 -0
  28. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/l4d2_data/players.py +0 -0
  29. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/l4d2_data/serverip.py +0 -0
  30. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/l4d2_file/__init__.py +0 -0
  31. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/l4d2_image/__init__.py +0 -0
  32. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/l4d2_image/download.py +0 -0
  33. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/l4d2_image/htmlimg.py +0 -0
  34. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/l4d2_image/send_image_tool.py +0 -0
  35. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/l4d2_image/steam.py +0 -0
  36. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/l4d2_image/vtfs.py +0 -0
  37. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/l4d2_queries/__init__.py +0 -0
  38. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/l4d2_queries/maps.py +0 -0
  39. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/l4d2_queries/ohter.py +0 -0
  40. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/l4d2_server/__init__.py +0 -0
  41. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/l4d2_server/rcon.py +0 -0
  42. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/l4d2_server/workshop.py +0 -0
  43. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/message.py +0 -0
  44. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/seach.py +0 -0
  45. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/txt_to_img.py +0 -0
  46. {nonebot_plugin_l4d2_server-0.3.5b1 → nonebot_plugin_l4d2_server-0.3.5b2}/nonebot_plugin_l4d2_server/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nonebot-plugin-l4d2-server
3
- Version: 0.3.5b1
3
+ Version: 0.3.5b2
4
4
  Summary: L4D2 server related operations plugin for NoneBot
5
5
  Home-page: https://github.com/Umamusume-Agnes-Digital/nonebot_plugin_l4d2_server
6
6
  License: MIT
@@ -22,9 +22,11 @@ Classifier: Programming Language :: Python :: 3.10
22
22
  Classifier: Programming Language :: Python :: 3.8
23
23
  Classifier: Programming Language :: Python :: 3.9
24
24
  Requires-Dist: aiohttp (>=3.8.3,<4.0.0)
25
+ Requires-Dist: amis-python (>=1.0.6,<2.0.0)
25
26
  Requires-Dist: asyncio (>=3.4.3,<4.0.0)
26
27
  Requires-Dist: beautifulsoup4 (>=4.8.0,<5.0.0)
27
28
  Requires-Dist: httpx (>=0.23.3,<0.24.0)
29
+ Requires-Dist: jieba (>=0.42.1,<0.43.0)
28
30
  Requires-Dist: jinja2 (>=3.0.0,<4.0.0)
29
31
  Requires-Dist: nonebot-adapter-onebot (>=2.1.5)
30
32
  Requires-Dist: nonebot2 (>=2.0.0rc3,<3.0.0)
@@ -37,6 +39,7 @@ Requires-Dist: python-a2s (>=1.3.0,<2.0.0)
37
39
  Requires-Dist: pyunpack (>=0.3.0,<0.4.0)
38
40
  Requires-Dist: rarfile (>=4.0,<5.0)
39
41
  Requires-Dist: rcon (>=2.1.0,<3.0.0)
42
+ Requires-Dist: ruamel.yaml (>=0.17.21,<0.18.0)
40
43
  Requires-Dist: srctools (>=2.3.9,<3.0.0)
41
44
  Project-URL: Repository, https://github.com/Umamusume-Agnes-Digital/nonebot_plugin_l4d2_server
42
45
  Description-Content-Type: text/markdown
@@ -510,13 +513,13 @@ ubuntu20.04 python3.10
510
513
 
511
514
  <h2 id="cx">服务器相关 </h2>
512
515
 
513
- 目前插件会内置经腐竹同意的服的ip
516
+ 目前插件会内置经腐竹同意的服的查询(未来将使用api)
514
517
 
515
518
  | 指令 | 服务器 | op | 数量 |
516
519
  |:-----:|:----:|:----:|:----:|
517
- | 云 | anne电信服云服 | 东 | 26
520
+ | 云 | anne电信服云服(暂时不提供) | 东 | 24
518
521
  | 呆呆 | 呆呆的小窝 | 提莫大魔王 | 15
519
- | 橘 | 橘希实香的小窝 | 橘希实香 | 4
522
+ | 橘 | 橘希实香的小窝 | 橘希实香 | 7
520
523
 
521
524
  如果需要上传自己的ip可以Pr、iss或者进qq群
522
525
 
@@ -467,13 +467,13 @@ ubuntu20.04 python3.10
467
467
 
468
468
  <h2 id="cx">服务器相关 </h2>
469
469
 
470
- 目前插件会内置经腐竹同意的服的ip
470
+ 目前插件会内置经腐竹同意的服的查询(未来将使用api)
471
471
 
472
472
  | 指令 | 服务器 | op | 数量 |
473
473
  |:-----:|:----:|:----:|:----:|
474
- | 云 | anne电信服云服 | 东 | 26
474
+ | 云 | anne电信服云服(暂时不提供) | 东 | 24
475
475
  | 呆呆 | 呆呆的小窝 | 提莫大魔王 | 15
476
- | 橘 | 橘希实香的小窝 | 橘希实香 | 4
476
+ | 橘 | 橘希实香的小窝 | 橘希实香 | 7
477
477
 
478
478
  如果需要上传自己的ip可以Pr、iss或者进qq群
479
479
 
@@ -16,6 +16,8 @@
16
16
  """
17
17
  from nonebot.matcher import Matcher
18
18
  from nonebot.typing import T_State
19
+ from nonebot.params import CommandArg,ArgPlainText,RegexGroup,Arg,Command,RawCommand
20
+
19
21
  from typing import Tuple,Union,List
20
22
  from time import sleep
21
23
  from .config import *
@@ -33,6 +35,7 @@ from .l4d2_queries.maps import seach_map,map_dict_to_str
33
35
  from .txt_to_img import mode_txt_to_img
34
36
  # from .l4d2_server import RCONClient
35
37
  from nonebot import get_bot, require
38
+ from .l4d2_server import web,webUI
36
39
  scheduler = require("nonebot_plugin_apscheduler").scheduler
37
40
 
38
41
  driver = get_driver()
@@ -1,5 +1,5 @@
1
- from nonebot import on_notice,on_command,on_regex,on_fullmatch,on_shell_command
2
- from nonebot.params import CommandArg,ArgPlainText,RegexGroup,Arg,Command,RawCommand
1
+ from nonebot import on_notice,on_command,on_regex,on_fullmatch,on_shell_command,on_keyword
2
+
3
3
  import re
4
4
  import nonebot
5
5
  from nonebot.permission import SUPERUSER
@@ -86,8 +86,8 @@ show_queries = on_command('showq',aliases={"求生订阅"},priority=20,block=Tru
86
86
  join_server = on_command('showq',aliases={"求生加入"},priority=20,block=True)
87
87
  connect_rcon = on_command("Rrcon", aliases={"求生连接", '求生链接','求生rcon'}, priority=50, block=False)
88
88
  end_connect = ['stop', '结束', '连接结束', '结束连接']
89
- search_api = on_command('search',aliases={'求生三方'}, priority=50, block=True)
90
-
89
+ search_api = on_command('search',aliases={'求生三方'}, priority=20, block=True)
90
+ which_map = on_keyword(("是什么图"),priority=20, block=False)
91
91
 
92
92
  # 下载内容
93
93
  up_workshop = on_command('workshop',aliases={'创意工坊下载','求生创意工坊'},priority=20,block=True)
@@ -1,5 +1,6 @@
1
1
  import nonebot
2
2
  from nonebot.permission import SUPERUSER
3
+ from nonebot import get_driver
3
4
  from nonebot.adapters.onebot.v11.permission import (
4
5
  GROUP_ADMIN,
5
6
  GROUP_OWNER,
@@ -17,7 +18,11 @@ except:
17
18
  file_format = (".vpk",".zip",".7z",'rar')
18
19
  # 权限
19
20
 
20
-
21
+ driver = get_driver()
22
+ try:
23
+ NICKNAME: str = list(driver.config.nickname)[0]
24
+ except Exception:
25
+ NICKNAME = 'bot'
21
26
  CHECK_FILE:int = 0
22
27
  ANNE_IP:dict = json.load(open(Path(__file__).parent.joinpath(
23
28
  'data/L4D2/l4d2.json'), "r", encoding="utf8"))
@@ -28,69 +33,69 @@ ADMINISTRATOR = SUPERUSER | GROUP_ADMIN | GROUP_OWNER | PRIVATE_FRIEND
28
33
  # file 填写求生服务器所在路径
29
34
 
30
35
  try:
31
- l4_file: List[str] = nonebot.get_driver().config.l4_file
36
+ l4_file: List[str] = driver.config.l4_file
32
37
  except:
33
38
  l4_file: List[str] = ['/home/ubuntu/l4d2']
34
39
 
35
40
 
36
41
  try:
37
- l4_image: bool = nonebot.get_driver().config.l4_image
42
+ l4_image: bool = driver.config.l4_image
38
43
  except:
39
44
  l4_image: bool = True
40
45
 
41
46
  try:
42
- l4_steamid: bool = nonebot.get_driver().config.l4_steamid
47
+ l4_steamid: bool = driver.config.l4_steamid
43
48
  except:
44
49
  l4_steamid: bool = True
45
50
 
46
51
  try:
47
- l4_only: bool = nonebot.get_driver().config.l4_only
52
+ l4_only: bool = driver.config.l4_only
48
53
  except:
49
54
  l4_only: bool = False
50
55
 
51
56
  try:
52
- l4_font: str = nonebot.get_driver().config.l4_font
57
+ l4_font: str = driver.config.l4_font
53
58
  except:
54
59
  l4_font: str = 'simsun.ttf'
55
60
 
56
61
  try:
57
- l4_host: List[str] = nonebot.get_driver().config.l4_host
62
+ l4_host: List[str] = driver.config.l4_host
58
63
  except:
59
64
  l4_host: List[str] = ['127.0.0.1']
60
65
 
61
66
 
62
67
  try:
63
- l4_port: List[str] = nonebot.get_driver().config.l4_port
68
+ l4_port: List[str] = driver.config.l4_port
64
69
  except:
65
70
  l4_port: List[str] = ['20715']
66
71
 
67
72
 
68
73
  try:
69
- l4_rcon: List[str] = nonebot.get_driver().config.l4_rcon
74
+ l4_rcon: List[str] = driver.config.l4_rcon
70
75
  except:
71
76
  l4_rcon: List[str] = ['114514']
72
77
 
73
78
 
74
79
  try:
75
- l4_master: List[str] = nonebot.get_driver().config.l4_master
80
+ l4_master: List[str] = driver.config.l4_master
76
81
  except:
77
82
  l4_master: List[str] = ['114514']
78
83
 
79
84
 
80
85
  try:
81
86
  l4_proxies: set = {
82
- 'http://':nonebot.get_driver().config.l4_proxies
87
+ 'http://':driver.config.l4_proxies
83
88
  }
84
89
  except:
85
90
  l4_proxies = ''
86
91
 
87
92
  try:
88
- l4_style:str = nonebot.get_driver().config.l4_style
93
+ l4_style:str = driver.config.l4_style
89
94
  except:
90
95
  l4_style:str = ''
91
96
 
92
97
  try:
93
- l4_key:str = nonebot.get_driver().config.l4_key
98
+ l4_key:str = driver.config.l4_key
94
99
  except:
95
100
  l4_key:str = ""
96
101
 
@@ -58,39 +58,59 @@
58
58
  {
59
59
  "id": 12,
60
60
  "version": "anne",
61
- "ip": "43.248.188.11:27022"
61
+ "ip": "103.219.39.254:20676"
62
62
  },
63
63
  {
64
64
  "id": 13,
65
65
  "version": "对抗",
66
- "ip": "43.248.188.11:36688"
66
+ "ip": "103.219.39.254:20518"
67
67
  },
68
68
  {
69
69
  "id": 14,
70
70
  "version": "对抗",
71
- "ip": "43.248.188.11:37688"
71
+ "ip": "103.219.39.254:28349"
72
72
  },
73
73
  {
74
74
  "id": 15,
75
75
  "version": "anne",
76
- "ip": "43.248.188.11:27023"
76
+ "ip": "103.219.39.254:23649"
77
77
  }
78
78
  ],
79
79
  "橘":[
80
80
  {
81
- "id":1,
81
+ "id": 1,
82
82
  "version": "战役",
83
83
  "ip":"202.189.7.139:12203"
84
84
  },
85
85
  {
86
- "id":2,
86
+ "id": 2,
87
87
  "version": "多特",
88
88
  "ip":"202.189.7.139:12202"
89
89
  },
90
90
  {
91
- "id":3,
91
+ "id": 3,
92
92
  "version": "多特",
93
93
  "ip":"202.189.7.139:12302"
94
+ },
95
+ {
96
+ "id": 4,
97
+ "version": "anne",
98
+ "ip": "g3.kok.wang:40024"
99
+ },
100
+ {
101
+ "id": 5,
102
+ "version": "anne",
103
+ "ip": "g3.kok.wang:40025"
104
+ },
105
+ {
106
+ "id": 6,
107
+ "version": "anne",
108
+ "ip": "g3.kok.wang:40026"
109
+ },
110
+ {
111
+ "id": 7,
112
+ "version": "anne",
113
+ "ip": "g3.kok.wang:40027"
94
114
  }
95
115
  ]
96
116
  }
@@ -247,7 +247,7 @@ async def write_json(data_str:str):
247
247
  return '删除成功喵'
248
248
  return '序号不正确,请输入【求生更新 删除 腐竹 序号】'
249
249
  return '腐竹名不存在,请输入【求生更新 删除 腐竹 序号】'
250
-
250
+ ip_anne_list=[]
251
251
  try:
252
252
  ips = ALL_HOST['云']
253
253
  ip_anne_list = []
@@ -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
+
@@ -0,0 +1,353 @@
1
+ from amis import ColumnList, AmisList, ActionType, TableCRUD, TableColumn
2
+ from amis import Dialog, PageSchema, Switch, InputNumber, InputTag, Action, App
3
+ from amis import Form, InputText, InputPassword, DisplayModeEnum, Horizontal, Remark, Html, Page, AmisAPI, Wrapper
4
+ from amis import LevelEnum, Select, InputArray, Alert, Tpl, Flex
5
+
6
+
7
+ from ..config import NICKNAME
8
+
9
+ logo = Html(html='''
10
+ <p align="center">
11
+ <a href="https://github.com/CMHopeSunshine/nonebot-plugin-learning-chat/">
12
+ <img src="http://static.cherishmoon.fun/LittlePaimon/readme/logo.png"
13
+ width="256" height="256" alt="Learning-Chat">
14
+ </a>
15
+ </p>
16
+ <h1 align="center">Nonebot-Plugin-Learning-Chat 控制台</h1>
17
+ <div align="center">
18
+ <a href="https://github.com/CMHopeSunshine/nonebot-plugin-learning-chat/" target="_blank">
19
+ Github仓库</a>
20
+ </div>
21
+ <br>
22
+ <br>
23
+ ''')
24
+ login_api = AmisAPI(
25
+ url='/learning_chat/api/login',
26
+ method='post',
27
+ adaptor='''
28
+ if (payload.status == 0) {
29
+ localStorage.setItem("token", payload.data.token);
30
+ }
31
+ return payload;
32
+ '''
33
+ )
34
+
35
+ login_form = Form(api=login_api, title='', body=[
36
+ InputText(name='username', label='用户名',
37
+ labelRemark=Remark(shape='circle', content='后台管理用户名,默认为chat')),
38
+ InputPassword(name='password', label='密码',
39
+ labelRemark=Remark(shape='circle', content='后台管理密码,默认为admin')),
40
+ ], mode=DisplayModeEnum.horizontal, horizontal=Horizontal(left=3, right=9, offset=5), redirect='/learning_chat/admin')
41
+ body = Wrapper(className='w-2/5 mx-auto my-0 m:w-full', body=login_form)
42
+ login_page = Page(title='', body=[logo, body])
43
+
44
+ global_config_form = Form(
45
+ title='全局配置',
46
+ name='global_config',
47
+ initApi='/learning_chat/api/chat_global_config',
48
+ api='post:/learning_chat/api/chat_global_config',
49
+ body=[
50
+ Switch(label='群聊学习总开关', name='total_enable', value='${total_enable}', onText='开启', offText='关闭',
51
+ labelRemark=Remark(shape='circle',
52
+ content='关闭后,全局都将不会再学习和回复(但是仍会对收到的消息进行记录)。')),
53
+ Switch(label='后台管理总开关', name='enable_web', value='${enable_web}', onText='开启', offText='关闭',
54
+ labelRemark=Remark(shape='circle',
55
+ content='是否开启本后台管理,若关闭,则无法再访问本页面。')),
56
+ InputText(label='后台管理用户名', name='web_username', value='${web_username}',
57
+ labelRemark=Remark(shape='circle',
58
+ content='登录本后台管理所需要的用户名。')),
59
+ InputPassword(label='后台管理密码', name='web_password', value='${web_password}',
60
+ labelRemark=Remark(shape='circle',
61
+ content='登录本后台管理所需要的密码。')),
62
+ InputText(label='后台管理token密钥', name='web_secret_key', value='${web_secret_key}',
63
+ labelRemark=Remark(shape='circle',
64
+ content='用于本后台管理加密验证token的密钥。')),
65
+ InputNumber(label='单句关键词数量', name='KEYWORDS_SIZE', value='${KEYWORDS_SIZE}', visibleOn='${total_enable}',
66
+ min=2,
67
+ labelRemark=Remark(shape='circle',
68
+ content='单句语句标签数量,影响对一句话的主题词提取效果,建议保持默认为3。')),
69
+ InputNumber(label='跨群回复阈值', name='cross_group_threshold', value='${cross_group_threshold}',
70
+ visibleOn='${total_enable}', min=1,
71
+ labelRemark=Remark(shape='circle',
72
+ content='当学习到的一种回复在N个群都有,那么这个回复就会变为全局回复。')),
73
+ InputNumber(label='最高学习次数', name='learn_max_count', value='${learn_max_count}',
74
+ visibleOn='${total_enable}', min=2, labelRemark=Remark(shape='circle',
75
+ content='学习的回复最高能累计到的次数,值越高,这个回复就会学习得越深,越容易进行回复,如果不想每次都大概率固定回复某一句话,可以将该值设低点。')),
76
+ InputTag(label='全局屏蔽词', name='ban_words', value='${ban_words}', enableBatchAdd=True,
77
+ placeholder='添加全局屏蔽词', visibleOn='${total_enable}', joinValues=False, extractValue=True,
78
+ labelRemark=Remark(shape='circle',
79
+ content='全局屏蔽词,含有这些词的消息不会学习和回复,默认已屏蔽at、分享、语音、和视频等消息。(回车进行添加)')),
80
+ InputTag(label='全局屏蔽用户', name='ban_users', value='${ban_users}',
81
+ enableBatchAdd=True,
82
+ placeholder='添加全局屏蔽用户', visibleOn='${total_enable}', joinValues=False, extractValue=True,
83
+ labelRemark=Remark(shape='circle',
84
+ content='全局屏蔽用户,和这些用户有关的消息不会学习和回复。(回车进行添加)')),
85
+ InputTag(label='自定义词典', name='dictionary', value='${dictionary}',
86
+ enableBatchAdd=True,
87
+ placeholder='添加自定义词语', visibleOn='${total_enable}', joinValues=False, extractValue=True,
88
+ labelRemark=Remark(shape='circle',
89
+ content='添加自定义词语,让分词能够识别未收录的词汇,提高学习的准确性。你可以添加特殊名词,这样学习时就会将该词看作一个整体,目前词典中已默认添加部分原神相关词汇。(回车进行添加)')),
90
+ ],
91
+ actions=[Action(label='保存', level=LevelEnum.success, type='submit'),
92
+ Action(label='重置', level=LevelEnum.warning, type='reset')]
93
+ )
94
+ group_select = Select(label='分群配置', name='group_id', source='${group_list}',
95
+ placeholder='选择群')
96
+ group_config_form = Form(
97
+ title='分群配置',
98
+ visibleOn='group_id != null',
99
+ initApi='/learning_chat/api/chat_group_config?group_id=${group_id}',
100
+ api='post:/learning_chat/api/chat_group_config?group_id=${group_id}',
101
+ body=[
102
+ Switch(label='群聊学习开关', name='enable', value='${enable}', onText='开启', offText='关闭',
103
+ labelRemark=Remark(shape='circle', content='针对该群的群聊学习开关,关闭后,仅该群不会学习和回复。')),
104
+ InputNumber(label='回复阈值', name='answer_threshold', value='${answer_threshold}', visibleOn='${enable}',
105
+ min=2,
106
+ labelRemark=Remark(shape='circle', content='可以理解为学习成功所需要的次数,值越低学得越快。')),
107
+ InputArray(label='回复阈值权重', name='answer_threshold_weights', value='${answer_threshold_weights}',
108
+ items=InputNumber(min=1, max=100, value=25, suffix='%'), inline=True, visibleOn='${enable}',
109
+ labelRemark=Remark(shape='circle',
110
+ content='影响回复阈值的计算方式,以默认的回复阈值4、权重[10, 30, 60]为例,在计算阈值时,60%概率为4,30%概率为3,10%概率为2。')),
111
+ InputNumber(label='复读阈值', name='repeat_threshold', value='${repeat_threshold}', visibleOn='${enable}',
112
+ min=2,
113
+ labelRemark=Remark(shape='circle',
114
+ content=f'跟随复读所需要的阈值,有N个人复读后,{NICKNAME}就会跟着复读。')),
115
+ InputNumber(label='打断复读概率', name='break_probability', value='${break_probability}',
116
+ min=0, max=100, suffix='%', visibleOn='${AND(enable, speak_enable)}',
117
+ labelRemark=Remark(shape='circle', content='达到复读阈值时,打断复读而不是跟随复读的概率。')),
118
+ InputTag(label='屏蔽词', name='ban_words', value='${ban_words}', enableBatchAdd=True,
119
+ placeholder='添加屏蔽词', visibleOn='${enable}', joinValues=False, extractValue=True,
120
+ labelRemark=Remark(shape='circle', content='含有这些词的消息不会学习和回复。(回车进行添加)')),
121
+ InputTag(label='屏蔽用户', source='${member_list}', name='ban_users', value='${ban_users}', enableBatchAdd=True,
122
+ placeholder='添加屏蔽用户', visibleOn='${enable}', joinValues=False, extractValue=True,
123
+ labelRemark=Remark(shape='circle', content='和该群中这些用户有关的消息不会学习和回复。(回车进行添加)')),
124
+ Switch(label='主动发言开关', name='speak_enable', value='${speak_enable}', visibleOn='${enable}',
125
+ labelRemark=Remark(shape='circle',
126
+ content=f'是否允许{NICKNAME}在该群主动发言,主动发言是指每隔一段时间挑选一个热度较高的群,主动发一些学习过的内容。')),
127
+ InputNumber(label='主动发言阈值', name='speak_threshold', value='${speak_threshold}',
128
+ visibleOn='${AND(enable, speak_enable)}', min=0,
129
+ labelRemark=Remark(shape='circle', content='值越低,主动发言的可能性越高。')),
130
+ InputNumber(label='主动发言最小间隔', name='speak_min_interval', value='${speak_min_interval}', min=0,
131
+ visibleOn='${AND(enable, speak_enable)}', suffix='秒',
132
+ labelRemark=Remark(shape='circle', content='进行主动发言的最小时间间隔。')),
133
+ InputNumber(label='连续主动发言概率', name='speak_continuously_probability',
134
+ value='${speak_continuously_probability}', min=0, max=100, suffix='%',
135
+ visibleOn='${AND(enable, speak_enable)}',
136
+ labelRemark=Remark(shape='circle', content='触发主动发言时,连续进行发言的概率。')),
137
+ InputNumber(label='最大连续主动发言句数', name='speak_continuously_max_len',
138
+ value='${speak_continuously_max_len}', visibleOn='${AND(enable, speak_enable)}', min=1,
139
+ labelRemark=Remark(shape='circle', content='连续主动发言的最大句数。')),
140
+ InputNumber(label='主动发言附带戳一戳概率', name='speak_poke_probability', value='${speak_poke_probability}',
141
+ min=0, max=100, suffix='%', visibleOn='${AND(enable, speak_enable)}',
142
+ labelRemark=Remark(shape='circle',
143
+ content='主动发言时附带戳一戳的概率,会在最近5个发言者中随机选一个戳。')),
144
+ ],
145
+ actions=[Action(label='保存', level=LevelEnum.success, type='submit'),
146
+ ActionType.Ajax(
147
+ label='保存至所有群',
148
+ level=LevelEnum.primary,
149
+ confirmText='确认将当前配置保存至所有群?',
150
+ api='post:/learning_chat/api/chat_group_config?group_id=all'
151
+ ),
152
+ Action(label='重置', level=LevelEnum.warning, type='reset')]
153
+ )
154
+
155
+ blacklist_table = TableCRUD(mode='table',
156
+ title='',
157
+ syncLocation=False,
158
+ api='/learning_chat/api/get_chat_blacklist',
159
+ interval=15000,
160
+ headerToolbar=[ActionType.Ajax(label='取消所有禁用',
161
+ level=LevelEnum.warning,
162
+ confirmText='确定要取消所有禁用吗?',
163
+ api='put:/learning_chat/api/delete_all?type=blacklist')],
164
+ itemActions=[ActionType.Ajax(tooltip='取消禁用',
165
+ icon='fa fa-check-circle-o text-info',
166
+ confirmText='取消该被禁用的内容/关键词,但是仍然需要重新学习哦!',
167
+ api='delete:/learning_chat/api/delete_chat?type=blacklist&id=${id}')
168
+ ],
169
+ footable=True,
170
+ columns=[TableColumn(type='tpl', tpl='${keywords|truncate:20}', label='内容/关键词',
171
+ name='keywords',
172
+ searchable=True, popOver={'mode': 'dialog', 'title': '全文',
173
+ 'className': 'break-all',
174
+ 'body': {'type': 'tpl',
175
+ 'tpl': '${keywords}'}}),
176
+ TableColumn(label='已禁用的群', name='bans', searchable=True),
177
+ ])
178
+ message_table = TableCRUD(mode='table',
179
+ title='',
180
+ syncLocation=False,
181
+ api='/learning_chat/api/get_chat_messages',
182
+ interval=12000,
183
+ headerToolbar=[ActionType.Ajax(label='删除所有聊天记录',
184
+ level=LevelEnum.warning,
185
+ confirmText='确定要删除所有聊天记录吗?',
186
+ api='put:/learning_chat/api/delete_all?type=message')],
187
+ itemActions=[ActionType.Ajax(tooltip='禁用',
188
+ icon='fa fa-ban text-danger',
189
+ confirmText='禁用该聊天记录相关的学习内容和回复',
190
+ api='put:/learning_chat/api/ban_chat?type=message&id=${id}'),
191
+ ActionType.Ajax(tooltip='删除',
192
+ icon='fa fa-times text-danger',
193
+ confirmText='删除该条聊天记录',
194
+ api='delete:/learning_chat/api/delete_chat?type=message&id=${id}')
195
+ ],
196
+ footable=True,
197
+ columns=[TableColumn(label='消息ID', name='message_id'),
198
+ TableColumn(label='群ID', name='group_id', searchable=True),
199
+ TableColumn(label='用户ID', name='user_id', searchable=True),
200
+ TableColumn(type='tpl', tpl='${raw_message|truncate:20}', label='消息',
201
+ name='message',
202
+ searchable=True, popOver={'mode': 'dialog', 'title': '消息全文',
203
+ 'className': 'break-all',
204
+ 'body': {'type': 'tpl',
205
+ 'tpl': '${raw_message}'}}),
206
+ TableColumn(type='tpl', tpl='${time|date:YYYY-MM-DD HH\\:mm\\:ss}', label='时间',
207
+ name='time', sortable=True)
208
+ ])
209
+ answer_table = TableCRUD(
210
+ mode='table',
211
+ syncLocation=False,
212
+ footable=True,
213
+ api='/learning_chat/api/get_chat_answers',
214
+ interval=12000,
215
+ headerToolbar=[ActionType.Ajax(label='删除所有已学习的回复',
216
+ level=LevelEnum.warning,
217
+ confirmText='确定要删除所有已学习的回复吗?',
218
+ api='put:/learning_chat/api/delete_all?type=answer')],
219
+ itemActions=[ActionType.Ajax(tooltip='禁用',
220
+ icon='fa fa-ban text-danger',
221
+ confirmText='禁用并删除该已学回复',
222
+ api='put:/learning_chat/api/ban_chat?type=answer&id=${id}'),
223
+ ActionType.Ajax(tooltip='删除',
224
+ icon='fa fa-times text-danger',
225
+ confirmText='仅删除该已学回复,不会禁用,所以依然能继续学',
226
+ api='delete:/learning_chat/api/delete_chat?type=answer&id=${id}')],
227
+ columns=[TableColumn(label='ID', name='id', visible=False),
228
+ TableColumn(label='群ID', name='group_id', searchable=True),
229
+ TableColumn(type='tpl', tpl='${keywords|truncate:20}', label='内容/关键词', name='keywords',
230
+ searchable=True, popOver={'mode': 'dialog', 'title': '内容全文', 'className': 'break-all',
231
+ 'body': {'type': 'tpl', 'tpl': '${keywords}'}}),
232
+ TableColumn(type='tpl', tpl='${time|date:YYYY-MM-DD HH\\:mm\\:ss}', label='最后学习时间', name='time',
233
+ sortable=True),
234
+ TableColumn(label='次数', name='count', sortable=True),
235
+ ColumnList(label='完整消息', name='messages', breakpoint='*', source='${messages}',
236
+ listItem=AmisList.Item(body={'name': 'msg'}))
237
+ ])
238
+ answer_table_on_context = TableCRUD(
239
+ mode='table',
240
+ syncLocation=False,
241
+ footable=True,
242
+ api='/learning_chat/api/get_chat_answers?context_id=${id}&page=${page}&perPage=${perPage}&orderBy=${orderBy}&orderDir=${orderDir}',
243
+ interval=12000,
244
+ headerToolbar=[ActionType.Ajax(label='删除该内容所有回复',
245
+ level=LevelEnum.warning,
246
+ confirmText='确定要删除该条内容已学习的回复吗?',
247
+ api='put:/learning_chat/api/delete_all?type=answer&id=${id}')],
248
+ itemActions=[ActionType.Ajax(tooltip='禁用',
249
+ icon='fa fa-ban text-danger',
250
+ confirmText='禁用并删除该已学回复',
251
+ api='put:/learning_chat/api/ban_chat?type=answer&id=${id}'),
252
+ ActionType.Ajax(tooltip='删除',
253
+ icon='fa fa-times text-danger',
254
+ confirmText='仅删除该已学回复,但不禁用,依然能继续学',
255
+ api='delete:/learning_chat/api/delete_chat?type=answer&id=${id}')],
256
+ columns=[TableColumn(label='ID', name='id', visible=False),
257
+ TableColumn(label='群ID', name='group_id'),
258
+ TableColumn(type='tpl', tpl='${keywords|truncate:20}', label='内容/关键词', name='keywords',
259
+ searchable=True, popOver={'mode': 'dialog', 'title': '内容全文', 'className': 'break-all',
260
+ 'body': {'type': 'tpl', 'tpl': '${keywords}'}}),
261
+ TableColumn(type='tpl', tpl='${time|date:YYYY-MM-DD HH\\:mm\\:ss}', label='最后学习时间', name='time',
262
+ sortable=True),
263
+ TableColumn(label='次数', name='count', sortable=True),
264
+ ColumnList(label='完整消息', name='messages', breakpoint='*', source='${messages}',
265
+ listItem=AmisList.Item(body={'name': 'msg'}))
266
+ ])
267
+ context_table = TableCRUD(mode='table',
268
+ title='',
269
+ syncLocation=False,
270
+ api='/learning_chat/api/get_chat_contexts',
271
+ interval=12000,
272
+ headerToolbar=[ActionType.Ajax(label='删除所有学习内容',
273
+ level=LevelEnum.warning,
274
+ confirmText='确定要删除所有已学习的内容吗?',
275
+ api='put:/learning_chat/api/delete_all?type=context')],
276
+ itemActions=[ActionType.Dialog(tooltip='回复列表',
277
+ icon='fa fa-book text-info',
278
+ dialog=Dialog(title='回复列表',
279
+ size='lg',
280
+ body=answer_table_on_context)),
281
+ ActionType.Ajax(tooltip='禁用',
282
+ icon='fa fa-ban text-danger',
283
+ confirmText='禁用并删除该学习的内容及其所有回复',
284
+ api='put:/learning_chat/api/ban_chat?type=context&id=${id}'),
285
+ ActionType.Ajax(tooltip='删除',
286
+ icon='fa fa-times text-danger',
287
+ confirmText='仅删除该学习的内容及其所有回复,但不禁用,依然能继续学',
288
+ api='delete:/learning_chat/api/delete_chat?type=context&id=${id}')
289
+ ],
290
+ footable=True,
291
+ columns=[TableColumn(label='ID', name='id', visible=False),
292
+ TableColumn(type='tpl', tpl='${keywords|truncate:20}', label='内容/关键词',
293
+ name='keywords', searchable=True,
294
+ popOver={'mode': 'dialog', 'title': '内容全文', 'className': 'break-all',
295
+ 'body': {'type': 'tpl', 'tpl': '${keywords}'}}),
296
+ TableColumn(type='tpl', tpl='${time|date:YYYY-MM-DD HH\\:mm\\:ss}',
297
+ label='最后学习时间', name='time', sortable=True),
298
+ TableColumn(label='已学次数', name='count', sortable=True),
299
+ ])
300
+
301
+ message_page = PageSchema(url='/messages', icon='fa fa-comments', label='群聊消息',
302
+ schema=Page(title='群聊消息', body=[
303
+ Alert(level=LevelEnum.info,
304
+ className='white-space-pre-wrap',
305
+ body=(f'此数据库记录了{NICKNAME}收到的聊天记录。\n'
306
+ '· 点击"禁用"可以将某条聊天记录进行禁用,这样其相关的学习就会列入禁用列表。\n'
307
+ '· 点击"删除"可以删除某条记录,但不会影响它的学习。\n'
308
+ f'· 可以通过搜索{NICKNAME}的QQ号,来查看它的回复记录。')),
309
+ message_table]))
310
+ context_page = PageSchema(url='/contexts', icon='fa fa-comment', label='学习内容',
311
+ schema=Page(title='内容',
312
+ body=[Alert(level=LevelEnum.info,
313
+ className='white-space-pre-wrap',
314
+ body=(f'此数据库记录了{NICKNAME}所学习的内容。\n'
315
+ '· 点击"回复列表"可以查看该条内容已学习到的可能的回复。\n'
316
+ '· 点击"禁用"可以将该学习进行禁用,以后不会再学。\n'
317
+ '· 点击"删除"可以删除该学习,让它重新开始学习这句话。')),
318
+ context_table]))
319
+ answer_page = PageSchema(url='/answers', icon='fa fa-commenting-o', label='内容回复',
320
+ schema=Page(title='回复',
321
+ body=[Alert(level=LevelEnum.info,
322
+ className='white-space-pre-wrap',
323
+ body=(
324
+ f'此数据库记录了{NICKNAME}已学习到的所有回复,但看不到这些回复属于哪些内容,推荐到"学习内容"表进行操作。\n'
325
+ '· 点击"禁用"可以将该回复进行禁用,以后不会再学。\n'
326
+ '· 点击"删除"可以删除该回复,让它重新开始学习。')),
327
+ answer_table]))
328
+ blacklist_page = PageSchema(url='/blacklist', icon='fa fa-ban', label='禁用列表',
329
+ schema=Page(title='禁用列表',
330
+ body=[Alert(level=LevelEnum.info,
331
+ className='white-space-pre-wrap',
332
+ body=f'此数据库记录了{NICKNAME}被禁用的内容/关键词。\n'
333
+ '· 可以取消禁用,使其能够重新继续学习。\n'
334
+ '· 不能在此添加禁用,只能在群中回复[不可以]或者在<配置>中添加屏蔽词来达到禁用效果。'),
335
+ blacklist_table]))
336
+ database_page = PageSchema(label='数据库', icon='fa fa-database',
337
+ children=[message_page, context_page, answer_page, blacklist_page])
338
+ config_page = PageSchema(url='/configs', isDefaultPage=True, icon='fa fa-wrench', label='配置',
339
+ schema=Page(title='配置', initApi='/learning_chat/api/get_group_list',
340
+ body=[global_config_form, group_select, group_config_form]))
341
+ chat_page = PageSchema(label='群聊学习', icon='fa fa-wechat (alias)', children=[config_page, database_page])
342
+
343
+ github_logo = Tpl(className='w-full',
344
+ tpl='<div class="flex justify-between"><div></div><div><a href="https://github.com/CMHopeSunshine/nonebot-plugin-learning-chat" target="_blank" title="Copyright"><i class="fa fa-github fa-2x"></i></a></div></div>')
345
+ header = Flex(className='w-full', justify='flex-end', alignItems='flex-end', items=[github_logo])
346
+
347
+ admin_app = App(brandName='Learning-Chat',
348
+ logo='http://static.cherishmoon.fun/LittlePaimon/readme/logo.png',
349
+ header=header,
350
+ pages=[{
351
+ 'children': [config_page, database_page]
352
+ }],
353
+ footer='<div class="p-2 text-center bg-blue-100">Copyright © 2021 - 2022 <a href="https://github.com/CMHopeSunshine/nonebot-plugin-learning-chat" target="_blank" class="link-secondary">Learning-Chat</a> X<a target="_blank" href="https://github.com/baidu/amis" class="link-secondary" rel="noopener"> amis v2.2.0</a></div>')
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "nonebot-plugin-l4d2-server"
3
- version = "0.3.5b1"
3
+ version = "0.3.5b2"
4
4
  description = "L4D2 server related operations plugin for NoneBot"
5
5
  authors = ["Agnes_Digital <Z735803792@163.com>"]
6
6
  license = "MIT"
@@ -40,7 +40,9 @@ pyunpack = "^0.3.0"
40
40
  rarfile = "^4.0"
41
41
  patool = "^1.12"
42
42
  python-a2s = "^1.3.0"
43
-
43
+ "ruamel.yaml" = "^0.17.21"
44
+ amis-python = "^1.0.6"
45
+ jieba = "^0.42.1"
44
46
 
45
47
  [build-system]
46
48
  requires = ["poetry-core>=1.0.0"]
@@ -19,9 +19,11 @@ package_data = \
19
19
 
20
20
  install_requires = \
21
21
  ['aiohttp>=3.8.3,<4.0.0',
22
+ 'amis-python>=1.0.6,<2.0.0',
22
23
  'asyncio>=3.4.3,<4.0.0',
23
24
  'beautifulsoup4>=4.8.0,<5.0.0',
24
25
  'httpx>=0.23.3,<0.24.0',
26
+ 'jieba>=0.42.1,<0.43.0',
25
27
  'jinja2>=3.0.0,<4.0.0',
26
28
  'nonebot-adapter-onebot>=2.1.5',
27
29
  'nonebot2>=2.0.0rc3,<3.0.0',
@@ -34,13 +36,14 @@ install_requires = \
34
36
  'pyunpack>=0.3.0,<0.4.0',
35
37
  'rarfile>=4.0,<5.0',
36
38
  'rcon>=2.1.0,<3.0.0',
39
+ 'ruamel.yaml>=0.17.21,<0.18.0',
37
40
  'srctools>=2.3.9,<3.0.0']
38
41
 
39
42
  setup_kwargs = {
40
43
  'name': 'nonebot-plugin-l4d2-server',
41
- 'version': '0.3.5b1',
44
+ 'version': '0.3.5b2',
42
45
  'description': 'L4D2 server related operations plugin for NoneBot',
43
- 'long_description': '<div align="center">\n <img src="https://s2.loli.net/2022/06/16/opBDE8Swad5rU3n.png" width="180" height="180" alt="NoneBotPluginLogo">\n <br>\n <p><img src="https://s2.loli.net/2022/06/16/xsVUGRrkbn1ljTD.png" width="240" alt="NoneBotPluginText"></p>\n</div>\n\n<div align="center">\n\n# nonebot_plugin_l4d2_server\n_✨Nonebot & Left 4 Dead 2 server操作✨_\n\n<a href="https://github.com/Umamusume-Agnes-Digital/nonebot_plugin_l4d2_server/stargazers">\n <img alt="GitHub stars" src="https://img.shields.io/github/stars/Umamusume-Agnes-Digital/nonebot_plugin_l4d2_server" alt="stars">\n</a>\n<a href="https://github.com/Umamusume-Agnes-Digital/nonebot_plugin_l4d2_server/issues">\n <img alt="GitHub issues" src="https://img.shields.io/github/issues/Umamusume-Agnes-Digital/nonebot_plugin_l4d2_server" alt="issues">\n</a>\n<a href="https://jq.qq.com/?_wv=1027&k=HdjoCcAe">\n <img src="https://img.shields.io/badge/QQ%E7%BE%A4-399365126-orange?style=flat-square" alt="QQ Chat Group">\n</a>\n<a href="https://pypi.python.org/pypi/nonebot_plugin_l4d2_server">\n <img src="https://img.shields.io/pypi/v/nonebot_plugin_l4d2_server.svg" alt="pypi">\n</a>\n<a href="https://pypi.python.org/pypi/nonebot_plugin_l4d2_server">\n <img src="https://img.shields.io/pypi/dm/nonebot_plugin_l4d2_server" alt="pypi download">\n</a>\n <img src="https://img.shields.io/badge/python-3.7+-blue.svg" alt="python">\n <img src="https://img.shields.io/badge/nonebot-2.0.0rc1+-red.svg" alt="NoneBot">\n</div>\n\n# 导航\n\n - [主要功能](#gn)\n - [安装](#az)\n - [env一键设置](#env)\n - [前置操作](#qz)\n - [指令](#zl)\n - [env参数](#cs)\n - [感谢](#ty)\n - [内置的查询ip](#cx)\n\n<h2 id="gn">主要功能</h2>\n\n- 求生服务器-本地多路径操作(传地图)\n- 批量查询指定ip服务器状态和玩家\n- 创意工坊下载和喷漆制作\n- [求生电信服anne](https://github.com/fantasylidong/CompetitiveWithAnne)[查询~](https://sb.trygek.com/l4d_stats/ranking/index.php)\n\n\n<h2 id="az">安装</h2>\n\n 1、nb plugin install nonebot_plugin_l4d2_server\n 2、pip install nonebot_plugin_l4d2_server\n 3、Download zip\n 4、git clone https://github.com/Umamusume-Agnes-Digital/nonebot_plugin_l4d2_server.git\n\n<h2 id="env">快速使用(env示例)</h2>\n\n # 复制到env文件里,没有默认就是下列值,如需修改安装下面env设置\n # 所有的多选,用逗号隔开\n l4_master = [\'1145149191\']\n l4_file = [\'/home/ubuntu/l4d2/coop\']\n l4_host = [\'127.0.0.1\']\n l4_port = [\'20715\']\n l4_rcon = [\'1145149191810\']\n l4_font = \'simsun.ttc\'\n l4_only = True\n\n<h2 id="qz">前置游戏操作 </h2>\n\n- 如果要操作求生服务器文件,机器人与求生服务器处于同一个服务器上\n\n- 如果你按照以下步骤操作,env配置可以不填\n\n- 创建一个steam求生服务器(预计需要储存14G)\n\n\n<details>\n<summary>展开/收起</summary>\n\n\n### 以ubuntu为例,具体教程建议自行搜索,其中路径可以自行替换\n\n- 安装32位运行库\n\n sudo apt-get update\n sudo apt-get upgrade\n sudo apt-get install lib32gcc1\n\n- 下载steam\n\n mkdir ~/steamcmd\n cd ~/steamcmd\n wget https://steamcdn-a.akamaihd.net/client/installer/steamcmd_linux.tar.gz\n tar -zxvf steamcmd_linux.tar.gz\n ./steamcmd.sh\n\n- 下载l4d2文件\n\n Steam> force_install_dir /home/ubuntu/coop\n Steam> login anonymous\n Steam>app_update 222860 validate\n出现Success! App ‘222860’ fully installed后,输入quit或者exit\n\n- 创建启动脚本\n\n sudo vi /home/ubuntu/coop/cfg/server.cfg\n写入\n\n hostname "xxx" //游戏服务器名(英文)\n rcon_password "114514" //rcon密码\n sv_steamgroup "114514" //Steam组号\n sv_steamgroup_exclusive 1 //将服务器设为Steam组私有\n sm_cvar sv_gametypes "coop"//设置游戏模式为合作\n //设为1可防止玩家加入感染者方,仅战役模式\n sm_cvar director_no_human_zombies "1"\n mp_gamemode "coop"//激活游戏模式为合作\n sm_cvar z_difficulty "Hard"//设置游戏难度为困难\n sv_tags "hidden" //防止DDOS\n sm_cvar sv_region 4// 设定服务器区域为亚洲\n sv_visiblemaxplayers 8 //服务器可见最大玩家数\n maxplayers 8 //最大玩家数\n\n:wq回车保存\n\n cd ~\n sudo vi start.sh\n\n在脚本里写入\n\n cd /home/ubuntu/l4d2\n sudo ./srcds_run -game left4dead2 -condebug -tickrate 60 +exec server.cfg +map c2m1_highway\n\n- 启动游戏\n\n cd ~\n sh start.sh\n\n</details>\n\n\n<h2 id="zl">🤔 功能(指令)</h2>\n\n\n### 服务器\n\n(被动)上传地图:设置l4_master后,在群内发送压缩包zip/vpk/7z/rar,就可以直接上传地图到服务器了\n 如果设置了管理员,那么在群里也响应\n [ip]格式为[127.0.0.1:20715]括号内\n\n求生帮助:获取简单的帮助列表\n\n<details>\n<summary>展开/收起</summary>\n\n| 指令 | 范围 | 用途 | 说明 |\n|:-----:|:----:|:----:|:----:|\n| 求生地图/查看求生地图 | 所有人 | 看图 | 获取当前路径下所有的vpk文件,并输出目录 |\n| (求生)地图删除[number] | 群管/超管 | 删图 | 根据求生地图列出的序号,删除地图,[number]可以在第二条消息内输入 |\n| 求生地图[number][改/改名][text] | 群管/超管 | 改图名 | [number]同上,text为更改后名称,如果没有.vpk后缀会自动加上 |\n| 求生服务器指令[text] | 群管/超管 | 控制台 | rcon连接求生服务器控制台,使用ip和passsword |\n| 求生路径 | 群管/超管 | 查看路径 | 查看当前服务器路径 |\n| 求生路径切换[number] | 群管/超管 | 切换路径 | 切换本地服务器路径 |\n\n### anne(电信服)\n\n| 指令 | 范围 | 用途 | 说明 |\n|:-----:|:----:|:----:|:----:|\n| 探监/坐牢/开牢 | 所有人 | 随机抽一个目标云服 | 探监是得分超过160的队伍\\n坐牢是缺人队伍\\n开牢是空人房间 |\n| 求生anne[text]/@/[None] | 所有人 | 查anne成绩 | [text]可以是:空白(则使用绑定信息)、昵称、steamid、@user |\n| 求生绑定/steam绑定/anne绑定[text] | 所有人 | 绑定steam信息 | [text]可以是:昵称、steamid |\n| 求生解绑/steam解绑/anne解绑 | 所有人 | 解绑steam信息 | 无 |\n| 云[number] | 所有人 | 云服信息 | 获取服务器状态和直连ip |\n\n\n### ip(服务器查询)\n\n| 指令 | 范围 | 用途 | 说明 |\n|:-----:|:----:|:----:|:----:|\n| 求生ip[ip] | 所有人 | 查指定服务器 | [text]格式为[127.0.0.1:20715]括号内,可以查询服务器玩家名字 |\n| 求生订阅[ip] | 所有人 | 查询订阅服务器状态 | 返回一个图片\\n显示群所有订阅的服务器名字、状态、地图、玩家名字 |\n| 求生加入[number] | 所有人 | 获取进服直链 | [number]为求生订阅所显示的开头序号 |\n| 求生添加订阅[ip] | 群管 | 群订阅添加 | 新增订阅ip,在下次订阅的时候可以显示 |\n| 求生取消订阅[number] | 所有人 | 群订阅取消 | [number]为求生订阅所显示的开头序号 |\n| 求生更新 添加 [tag] [ip] [text] ([number]) | 群管 | 全局订阅添加 | 可以使用tag(+text)(+number)快速索引服务 |\n| 求生更新 删除 [tag] [number] | 群管 | 全局订阅添加 | [number]为求生订阅标记的默认序号 |\n| 求生更新 | 群管 | 刷新缓存 | 一般用不到,如果卡指令可能有用 |\n\n### 其他功能\n\n| 指令 | 范围 | 用途 | 说明 |\n|:-----:|:----:|:----:|:----:|\n| 创意工坊下载[text] | 所有人 | 下载创意工坊文件 | [text]为id或者网页url |\n| 求生喷漆 | 所有人 | 制作一个喷漆 | 只支持图片暂不支持gif |\n\n</details>\n\n<h2 id="cs">✅ env配置</h2>\n\n<details>\n<summary>展开/收起</summary>\n\n### 本地服务器相关\n| 配置项 | 必填 | 默认值 | 说明 |\n|:-----:|:----:|:----:|:----:|\n| l4_master | 是 | [\'1145149191\'] | list,里面是可以在群里传求生地图的qq号\n| l4_file | 否 | ["/home/ubuntu/l4d2/coop"] | 输入求生服务器的绝对路径,该目录下有游戏启动程序srcds_run |\n| l4_host | 否 | [\'127.0.0.1\'] | 服务器ip,如果是本机一般就是默认 |\n| l4_port | 否 | [\'20715\'] | 服务器端口号 |\n| l4_rcon | 否 | [\'114514\'] | 服务器的rcon密码 |\n\n### 可选填写\n| 配置项 | 必填 | 默认值 | 说明 |\n|:-----:|:----:|:----:|:----:|\n| l4_steamid | 否 | False | 布尔值,默认在输出时隐藏steamid,需要则设置为True |\n| l4_image | 否 | True | 布尔值,是否显示图片 |\n| l4_font | 否 | \'simsun.ttc\' | str,确保在开启图片的时候,字体存在 |\n| l4_only | 否 | False | 布尔值,如果不想在下载的时候阻碍其他指令可以开启,但是有不能下载超过200m地图的bug |\n| l4_style | 否 | \'\' | str,图片风格,目前可选[\'balck\'] |\n\n\n</details>\n\n## ✨ 效果展示\n\n订阅:<br>\n![ip](image/ip_server.png)<br>\nanne:<br>\n![anne](image/anne.png)<br>\n群聊:<br>\n![list](image/list.png)<br>\n私聊:<br>\n![up](image/up.png)<br>\n\n## 🤔 FAQ\n\n Q:UnicodeDecodeError: \'utf-8\' codec can\'t decode byte 0xd0 in position xxx: invalid continuation byte</b>\n\n A:说明rcon连接服务器时返回了乱码,有可能读取的信息包含表情包或者其他不明字符,我的方法是找到本地pypi包的rcon,编辑rcon/source/async_rcon.py,找到return response.payload.decode(encoding)并替换为return response.payload.decode(encoding,errors=\'ignore\')\n\n\n Q:ModuleNotFoundError: No module named \'_lzma\'\n\n A:python3.3版本后常见,解决方法是\n vim /usr/local/lib/python3.10/lzma.py\n #修改前\n from _lzma import *\n from _lzma import _encode_filter_properties, _decode_filter_properties\n #修改后 \n try:\n from _lzma import *\n from _lzma import _encode_filter_properties, _decode_filter_properties\n except ImportError:\n from backports.lzma import *\n from backports.lzma import _encode_filter_properties, _decode_filter_properties\n\n - 解压功能\n \n # rar压缩包\n # win直接下winRAR软件就可以\n # Ubuntu 和 Debian\n sudo wget https://www.rarlab.com/rar/rarlinux-x64-621b1.tar.gz\n sudo tar -xzpvf rarlinux-x64-621b1.tar.gz\n cd rar\n sudo make\n\n\n## 📝 TODO LIST\n\n<details>\n<summary>展开/收起</summary>\n\n- [ ] 帮助命令\n- [x] 创意工坊内容下载并上传q群\n- [ ] 求生每日签到/抽签\n- [ ] 按照数值自定义绘画信息图片\n- [ ] 支持直接修改本地cfg文件\n- [ ] ~支持远程连接求生服务器并操作~\n\n</details>\n\n## 🐛 已知BUG\n\n<details>\n<summary>展开/收起</summary>\n\n- [ ] 更改地图名称后,排序会错误\n- [ ] 求生喷漆可加载但无法输出\n\n</details>\n\n## 🔖 更新日志\n\n<details>\n<summary>展开/收起</summary>\n\n### 0.3.5--2022.3\n\n - 新增ping查询(在ip里包括)\n - 新增api查询(未完成)\n\n### 0.3.4--2022.3.1\n\n - 新增本地插件smx查询\n - 增加了三个内置群服\n - 修改了图片的UI,变好看了\n - 删减了部分图片和字体,使得轻量化\n - 修复了海量bug\n - 修复了python3.8中typing错误\n\n### 0.3.3--2022.2.26\n\n - 重写协议,使用a2s库,同时解决win端不同报错无法输出\n - 重~抄~写服务器查询UI,解决了不好看的问题\n - 从win测试,解决了一些win特有的bug\n - 重写服务器查询~还得是json~\n - 内置服务器查询系统,可以通过[服务器简称]+[number]/[模式]来访问\n - 新增批量查询服务器,不带参数则返回图片\n\n### 0.3.1--2022.2.22\n\n - 修复了路径识别为str对象的错误\n - 修复了初始化找不到文件的错误\n - 修复了路径拼接错误\n - 在win端成功测试,修复压缩包bug\n - 新增开关协程异步env设置\n - 测试rcon建立通讯\n - 实现切换路径查看地图和使用rcon指令\n\n### 0.3.0--2022.2.18\n\n - 修改了新的env配置,使得支持本地多服务器操作\n - 彻底解决了压缩包解压linux端的问题\n - 解决了win端默认gbk解码的错误\n - 解决rcon指令字体报错\n\n### 0.2.5--2022.2.10\n\n - 修复了依赖不足的bug\n - 更新了电信服战绩个人图片UI\n - 更新了批量服务器查看的UI\n - 修改了传文件为协程异步\n - 优化了部分rcon指令\n - ~tnd7z怎么不去死啊~使用pyunpack库解压7z\n\n### 0.2.4--2022.2.8\n\n - 使用poetry修复了pip安装文件缺失的bug\n\n### 0.2.3--2022.2.7\n\n - 新增坐牢和开牢\n - 修改了获取资源为异步协程却阻碍其他指令的bug\n - 新增json统计部分已知服务器(未来应该独立成库持续更新,如果把您的非公开服记录请联系我删除)\n - 喷剂制作开摆了,推测需要c/c++环境\n - 修改抽取文案\n - 新增查询服务器状态时返回connect ip\n - 修复了服务器查询无响应的时候,因为报错无回复信息的bug\n - 个人信息重置测试代码,下个版本更新\n - 新增求生更新添加和删除\n\n### 0.2.2--2022.2.1\n\n - 新增探监\n - 新增喷漆制作\n - 修复了魔改服务器导致解包错误的bug(就是直接忽略了)\n - 修改了部分对话响应\n\n### 0.2.1--2022.1.25\n\n - 新增电信服获取(东哥的肯定)\n - 优化图片UI \n - 新增云服快捷查询\n - 修复了因为没用玩家,导致的服务器状态查询错误\n - 新增电信服ip爬取(仅仅作为单次更新ip列表)\n\n### 0.2.0--2022.1.21\n\n - 新增创意工坊查询\n - 优化查询图片UI\n - 新增创意工坊文件下载\n - 修复了因为电信服官网前端修改导致查询失败的BUG\n\n### 0.1.7--2022.1.19\n\n - 新增群ip订阅,批量查询\n - 新增图片显示ip状态\n - 修复了因为玩家名字特殊字符导致的utf-8解码错误\n - 更新自己的第三方库VSQ==0.0.6\n\n### 0.1.6--2022.1.15\n\n - 新增ip查询服务器提供玩家数量和名字\n - 增加协程函数修复因为加载顺序导致的错误\n - 更新自己的第三方库VSQ==0.0.4\n\n### 0.1.5--2022.1.15\n\n- 新增服务器控制台指令,新增依赖rcon\n- 重新了数据库,不再使用json而是使用sql3\n- 改写了求生anne信息显示方式:如果单个数据以图片显示,如果多个数据以文字显示\n\n### 0.1.4--2022.1.9\n\n- 新增求生anne详情(看排名)\n- 所有的请求改为httpx\n- 更新了anne信息图片\n- 可选使用模拟谷歌浏览器来获取anne更多数据(~有点屎了,希望大佬救救~)\n\n### 0.1.3--2022.1.7\n\n- 新增绑定昵称和steamid\n- 新增可以艾特人查询anne成绩\n- 新增解绑信息\n\n### 0.1.2--2022.1.6\n\n- 新增支持图片输出\n- 新增查询anne服数据\n\n### 0.1.1--2022.1.5\n\n- 新增删除地图\n- 新增地图改名\n- 新增支持图片输出\n\n### 0.1.0--2022.1.4\n\n- 集中修复了Bug\n\n### 0.0.9--2022.1.4\n\n- 新增上传地图后,检测对比回复新地图名字\n- 修复中文名乱码问题\n\n### 0.0.8--2022.1.4\n\n- 支持vpk格式地图\n- 支持查看所有vpk格式文件\n\n### 0.0.6--2022.1.3\n\n- 修复了7z压缩包的方式,优化代码\n\n### 0.0.1--2022.1.3\n\n- 插件初次发布,可私聊添加地图\n\n</details>\n\n## ✨ 开发环境\nwin10 python3.9 <br>\nubuntu20.04 python3.10\n\n## 📖许可证GPL3.0\n\n This project is licensed under the GNU General Public License v3.0. See the LICENSE file for more information.\n\n\n<h2 id="ty">🌐感谢</h2>\n\n- 1、[修仙插件的数据库写法](https://github.com/s52047qwas/nonebot_plugin_xiuxian)\n- 2、~[自己写的求生之路查询库](https://github.com/Umamusume-Agnes-Digital/VSQ)~\n- 3、[可爱小Q的帮助(太好看了tql)](https://github.com/MeetWq/mybot)\n- 4、感谢petpet交流群各位大佬指点nonebot2事件处理api\n- 5、ArcPav改的暗♂黑风格菜单\n\n<h2 id="cx">服务器相关 </h2>\n\n目前插件会内置经腐竹同意的服的ip\n\n| 指令 | 服务器 | op | 数量 |\n|:-----:|:----:|:----:|:----:|\n| 云 | anne电信服云服 | 东 | 26\n| 呆呆 | 呆呆的小窝 | 提莫大魔王 | 15\n| 橘 | 橘希实香的小窝 | 橘希实香 | 4\n\n如果需要上传自己的ip可以Pr、iss或者进qq群\n\n[pr修改这个文件](https://github.com/Umamusume-Agnes-Digital/nonebot_plugin_l4d2_server/blob/main/nonebot_plugin_l4d2_server/data/L4D2/l4d2.json)\n',
46
+ 'long_description': '<div align="center">\n <img src="https://s2.loli.net/2022/06/16/opBDE8Swad5rU3n.png" width="180" height="180" alt="NoneBotPluginLogo">\n <br>\n <p><img src="https://s2.loli.net/2022/06/16/xsVUGRrkbn1ljTD.png" width="240" alt="NoneBotPluginText"></p>\n</div>\n\n<div align="center">\n\n# nonebot_plugin_l4d2_server\n_✨Nonebot & Left 4 Dead 2 server操作✨_\n\n<a href="https://github.com/Umamusume-Agnes-Digital/nonebot_plugin_l4d2_server/stargazers">\n <img alt="GitHub stars" src="https://img.shields.io/github/stars/Umamusume-Agnes-Digital/nonebot_plugin_l4d2_server" alt="stars">\n</a>\n<a href="https://github.com/Umamusume-Agnes-Digital/nonebot_plugin_l4d2_server/issues">\n <img alt="GitHub issues" src="https://img.shields.io/github/issues/Umamusume-Agnes-Digital/nonebot_plugin_l4d2_server" alt="issues">\n</a>\n<a href="https://jq.qq.com/?_wv=1027&k=HdjoCcAe">\n <img src="https://img.shields.io/badge/QQ%E7%BE%A4-399365126-orange?style=flat-square" alt="QQ Chat Group">\n</a>\n<a href="https://pypi.python.org/pypi/nonebot_plugin_l4d2_server">\n <img src="https://img.shields.io/pypi/v/nonebot_plugin_l4d2_server.svg" alt="pypi">\n</a>\n<a href="https://pypi.python.org/pypi/nonebot_plugin_l4d2_server">\n <img src="https://img.shields.io/pypi/dm/nonebot_plugin_l4d2_server" alt="pypi download">\n</a>\n <img src="https://img.shields.io/badge/python-3.7+-blue.svg" alt="python">\n <img src="https://img.shields.io/badge/nonebot-2.0.0rc1+-red.svg" alt="NoneBot">\n</div>\n\n# 导航\n\n - [主要功能](#gn)\n - [安装](#az)\n - [env一键设置](#env)\n - [前置操作](#qz)\n - [指令](#zl)\n - [env参数](#cs)\n - [感谢](#ty)\n - [内置的查询ip](#cx)\n\n<h2 id="gn">主要功能</h2>\n\n- 求生服务器-本地多路径操作(传地图)\n- 批量查询指定ip服务器状态和玩家\n- 创意工坊下载和喷漆制作\n- [求生电信服anne](https://github.com/fantasylidong/CompetitiveWithAnne)[查询~](https://sb.trygek.com/l4d_stats/ranking/index.php)\n\n\n<h2 id="az">安装</h2>\n\n 1、nb plugin install nonebot_plugin_l4d2_server\n 2、pip install nonebot_plugin_l4d2_server\n 3、Download zip\n 4、git clone https://github.com/Umamusume-Agnes-Digital/nonebot_plugin_l4d2_server.git\n\n<h2 id="env">快速使用(env示例)</h2>\n\n # 复制到env文件里,没有默认就是下列值,如需修改安装下面env设置\n # 所有的多选,用逗号隔开\n l4_master = [\'1145149191\']\n l4_file = [\'/home/ubuntu/l4d2/coop\']\n l4_host = [\'127.0.0.1\']\n l4_port = [\'20715\']\n l4_rcon = [\'1145149191810\']\n l4_font = \'simsun.ttc\'\n l4_only = True\n\n<h2 id="qz">前置游戏操作 </h2>\n\n- 如果要操作求生服务器文件,机器人与求生服务器处于同一个服务器上\n\n- 如果你按照以下步骤操作,env配置可以不填\n\n- 创建一个steam求生服务器(预计需要储存14G)\n\n\n<details>\n<summary>展开/收起</summary>\n\n\n### 以ubuntu为例,具体教程建议自行搜索,其中路径可以自行替换\n\n- 安装32位运行库\n\n sudo apt-get update\n sudo apt-get upgrade\n sudo apt-get install lib32gcc1\n\n- 下载steam\n\n mkdir ~/steamcmd\n cd ~/steamcmd\n wget https://steamcdn-a.akamaihd.net/client/installer/steamcmd_linux.tar.gz\n tar -zxvf steamcmd_linux.tar.gz\n ./steamcmd.sh\n\n- 下载l4d2文件\n\n Steam> force_install_dir /home/ubuntu/coop\n Steam> login anonymous\n Steam>app_update 222860 validate\n出现Success! App ‘222860’ fully installed后,输入quit或者exit\n\n- 创建启动脚本\n\n sudo vi /home/ubuntu/coop/cfg/server.cfg\n写入\n\n hostname "xxx" //游戏服务器名(英文)\n rcon_password "114514" //rcon密码\n sv_steamgroup "114514" //Steam组号\n sv_steamgroup_exclusive 1 //将服务器设为Steam组私有\n sm_cvar sv_gametypes "coop"//设置游戏模式为合作\n //设为1可防止玩家加入感染者方,仅战役模式\n sm_cvar director_no_human_zombies "1"\n mp_gamemode "coop"//激活游戏模式为合作\n sm_cvar z_difficulty "Hard"//设置游戏难度为困难\n sv_tags "hidden" //防止DDOS\n sm_cvar sv_region 4// 设定服务器区域为亚洲\n sv_visiblemaxplayers 8 //服务器可见最大玩家数\n maxplayers 8 //最大玩家数\n\n:wq回车保存\n\n cd ~\n sudo vi start.sh\n\n在脚本里写入\n\n cd /home/ubuntu/l4d2\n sudo ./srcds_run -game left4dead2 -condebug -tickrate 60 +exec server.cfg +map c2m1_highway\n\n- 启动游戏\n\n cd ~\n sh start.sh\n\n</details>\n\n\n<h2 id="zl">🤔 功能(指令)</h2>\n\n\n### 服务器\n\n(被动)上传地图:设置l4_master后,在群内发送压缩包zip/vpk/7z/rar,就可以直接上传地图到服务器了\n 如果设置了管理员,那么在群里也响应\n [ip]格式为[127.0.0.1:20715]括号内\n\n求生帮助:获取简单的帮助列表\n\n<details>\n<summary>展开/收起</summary>\n\n| 指令 | 范围 | 用途 | 说明 |\n|:-----:|:----:|:----:|:----:|\n| 求生地图/查看求生地图 | 所有人 | 看图 | 获取当前路径下所有的vpk文件,并输出目录 |\n| (求生)地图删除[number] | 群管/超管 | 删图 | 根据求生地图列出的序号,删除地图,[number]可以在第二条消息内输入 |\n| 求生地图[number][改/改名][text] | 群管/超管 | 改图名 | [number]同上,text为更改后名称,如果没有.vpk后缀会自动加上 |\n| 求生服务器指令[text] | 群管/超管 | 控制台 | rcon连接求生服务器控制台,使用ip和passsword |\n| 求生路径 | 群管/超管 | 查看路径 | 查看当前服务器路径 |\n| 求生路径切换[number] | 群管/超管 | 切换路径 | 切换本地服务器路径 |\n\n### anne(电信服)\n\n| 指令 | 范围 | 用途 | 说明 |\n|:-----:|:----:|:----:|:----:|\n| 探监/坐牢/开牢 | 所有人 | 随机抽一个目标云服 | 探监是得分超过160的队伍\\n坐牢是缺人队伍\\n开牢是空人房间 |\n| 求生anne[text]/@/[None] | 所有人 | 查anne成绩 | [text]可以是:空白(则使用绑定信息)、昵称、steamid、@user |\n| 求生绑定/steam绑定/anne绑定[text] | 所有人 | 绑定steam信息 | [text]可以是:昵称、steamid |\n| 求生解绑/steam解绑/anne解绑 | 所有人 | 解绑steam信息 | 无 |\n| 云[number] | 所有人 | 云服信息 | 获取服务器状态和直连ip |\n\n\n### ip(服务器查询)\n\n| 指令 | 范围 | 用途 | 说明 |\n|:-----:|:----:|:----:|:----:|\n| 求生ip[ip] | 所有人 | 查指定服务器 | [text]格式为[127.0.0.1:20715]括号内,可以查询服务器玩家名字 |\n| 求生订阅[ip] | 所有人 | 查询订阅服务器状态 | 返回一个图片\\n显示群所有订阅的服务器名字、状态、地图、玩家名字 |\n| 求生加入[number] | 所有人 | 获取进服直链 | [number]为求生订阅所显示的开头序号 |\n| 求生添加订阅[ip] | 群管 | 群订阅添加 | 新增订阅ip,在下次订阅的时候可以显示 |\n| 求生取消订阅[number] | 所有人 | 群订阅取消 | [number]为求生订阅所显示的开头序号 |\n| 求生更新 添加 [tag] [ip] [text] ([number]) | 群管 | 全局订阅添加 | 可以使用tag(+text)(+number)快速索引服务 |\n| 求生更新 删除 [tag] [number] | 群管 | 全局订阅添加 | [number]为求生订阅标记的默认序号 |\n| 求生更新 | 群管 | 刷新缓存 | 一般用不到,如果卡指令可能有用 |\n\n### 其他功能\n\n| 指令 | 范围 | 用途 | 说明 |\n|:-----:|:----:|:----:|:----:|\n| 创意工坊下载[text] | 所有人 | 下载创意工坊文件 | [text]为id或者网页url |\n| 求生喷漆 | 所有人 | 制作一个喷漆 | 只支持图片暂不支持gif |\n\n</details>\n\n<h2 id="cs">✅ env配置</h2>\n\n<details>\n<summary>展开/收起</summary>\n\n### 本地服务器相关\n| 配置项 | 必填 | 默认值 | 说明 |\n|:-----:|:----:|:----:|:----:|\n| l4_master | 是 | [\'1145149191\'] | list,里面是可以在群里传求生地图的qq号\n| l4_file | 否 | ["/home/ubuntu/l4d2/coop"] | 输入求生服务器的绝对路径,该目录下有游戏启动程序srcds_run |\n| l4_host | 否 | [\'127.0.0.1\'] | 服务器ip,如果是本机一般就是默认 |\n| l4_port | 否 | [\'20715\'] | 服务器端口号 |\n| l4_rcon | 否 | [\'114514\'] | 服务器的rcon密码 |\n\n### 可选填写\n| 配置项 | 必填 | 默认值 | 说明 |\n|:-----:|:----:|:----:|:----:|\n| l4_steamid | 否 | False | 布尔值,默认在输出时隐藏steamid,需要则设置为True |\n| l4_image | 否 | True | 布尔值,是否显示图片 |\n| l4_font | 否 | \'simsun.ttc\' | str,确保在开启图片的时候,字体存在 |\n| l4_only | 否 | False | 布尔值,如果不想在下载的时候阻碍其他指令可以开启,但是有不能下载超过200m地图的bug |\n| l4_style | 否 | \'\' | str,图片风格,目前可选[\'balck\'] |\n\n\n</details>\n\n## ✨ 效果展示\n\n订阅:<br>\n![ip](image/ip_server.png)<br>\nanne:<br>\n![anne](image/anne.png)<br>\n群聊:<br>\n![list](image/list.png)<br>\n私聊:<br>\n![up](image/up.png)<br>\n\n## 🤔 FAQ\n\n Q:UnicodeDecodeError: \'utf-8\' codec can\'t decode byte 0xd0 in position xxx: invalid continuation byte</b>\n\n A:说明rcon连接服务器时返回了乱码,有可能读取的信息包含表情包或者其他不明字符,我的方法是找到本地pypi包的rcon,编辑rcon/source/async_rcon.py,找到return response.payload.decode(encoding)并替换为return response.payload.decode(encoding,errors=\'ignore\')\n\n\n Q:ModuleNotFoundError: No module named \'_lzma\'\n\n A:python3.3版本后常见,解决方法是\n vim /usr/local/lib/python3.10/lzma.py\n #修改前\n from _lzma import *\n from _lzma import _encode_filter_properties, _decode_filter_properties\n #修改后 \n try:\n from _lzma import *\n from _lzma import _encode_filter_properties, _decode_filter_properties\n except ImportError:\n from backports.lzma import *\n from backports.lzma import _encode_filter_properties, _decode_filter_properties\n\n - 解压功能\n \n # rar压缩包\n # win直接下winRAR软件就可以\n # Ubuntu 和 Debian\n sudo wget https://www.rarlab.com/rar/rarlinux-x64-621b1.tar.gz\n sudo tar -xzpvf rarlinux-x64-621b1.tar.gz\n cd rar\n sudo make\n\n\n## 📝 TODO LIST\n\n<details>\n<summary>展开/收起</summary>\n\n- [ ] 帮助命令\n- [x] 创意工坊内容下载并上传q群\n- [ ] 求生每日签到/抽签\n- [ ] 按照数值自定义绘画信息图片\n- [ ] 支持直接修改本地cfg文件\n- [ ] ~支持远程连接求生服务器并操作~\n\n</details>\n\n## 🐛 已知BUG\n\n<details>\n<summary>展开/收起</summary>\n\n- [ ] 更改地图名称后,排序会错误\n- [ ] 求生喷漆可加载但无法输出\n\n</details>\n\n## 🔖 更新日志\n\n<details>\n<summary>展开/收起</summary>\n\n### 0.3.5--2022.3\n\n - 新增ping查询(在ip里包括)\n - 新增api查询(未完成)\n\n### 0.3.4--2022.3.1\n\n - 新增本地插件smx查询\n - 增加了三个内置群服\n - 修改了图片的UI,变好看了\n - 删减了部分图片和字体,使得轻量化\n - 修复了海量bug\n - 修复了python3.8中typing错误\n\n### 0.3.3--2022.2.26\n\n - 重写协议,使用a2s库,同时解决win端不同报错无法输出\n - 重~抄~写服务器查询UI,解决了不好看的问题\n - 从win测试,解决了一些win特有的bug\n - 重写服务器查询~还得是json~\n - 内置服务器查询系统,可以通过[服务器简称]+[number]/[模式]来访问\n - 新增批量查询服务器,不带参数则返回图片\n\n### 0.3.1--2022.2.22\n\n - 修复了路径识别为str对象的错误\n - 修复了初始化找不到文件的错误\n - 修复了路径拼接错误\n - 在win端成功测试,修复压缩包bug\n - 新增开关协程异步env设置\n - 测试rcon建立通讯\n - 实现切换路径查看地图和使用rcon指令\n\n### 0.3.0--2022.2.18\n\n - 修改了新的env配置,使得支持本地多服务器操作\n - 彻底解决了压缩包解压linux端的问题\n - 解决了win端默认gbk解码的错误\n - 解决rcon指令字体报错\n\n### 0.2.5--2022.2.10\n\n - 修复了依赖不足的bug\n - 更新了电信服战绩个人图片UI\n - 更新了批量服务器查看的UI\n - 修改了传文件为协程异步\n - 优化了部分rcon指令\n - ~tnd7z怎么不去死啊~使用pyunpack库解压7z\n\n### 0.2.4--2022.2.8\n\n - 使用poetry修复了pip安装文件缺失的bug\n\n### 0.2.3--2022.2.7\n\n - 新增坐牢和开牢\n - 修改了获取资源为异步协程却阻碍其他指令的bug\n - 新增json统计部分已知服务器(未来应该独立成库持续更新,如果把您的非公开服记录请联系我删除)\n - 喷剂制作开摆了,推测需要c/c++环境\n - 修改抽取文案\n - 新增查询服务器状态时返回connect ip\n - 修复了服务器查询无响应的时候,因为报错无回复信息的bug\n - 个人信息重置测试代码,下个版本更新\n - 新增求生更新添加和删除\n\n### 0.2.2--2022.2.1\n\n - 新增探监\n - 新增喷漆制作\n - 修复了魔改服务器导致解包错误的bug(就是直接忽略了)\n - 修改了部分对话响应\n\n### 0.2.1--2022.1.25\n\n - 新增电信服获取(东哥的肯定)\n - 优化图片UI \n - 新增云服快捷查询\n - 修复了因为没用玩家,导致的服务器状态查询错误\n - 新增电信服ip爬取(仅仅作为单次更新ip列表)\n\n### 0.2.0--2022.1.21\n\n - 新增创意工坊查询\n - 优化查询图片UI\n - 新增创意工坊文件下载\n - 修复了因为电信服官网前端修改导致查询失败的BUG\n\n### 0.1.7--2022.1.19\n\n - 新增群ip订阅,批量查询\n - 新增图片显示ip状态\n - 修复了因为玩家名字特殊字符导致的utf-8解码错误\n - 更新自己的第三方库VSQ==0.0.6\n\n### 0.1.6--2022.1.15\n\n - 新增ip查询服务器提供玩家数量和名字\n - 增加协程函数修复因为加载顺序导致的错误\n - 更新自己的第三方库VSQ==0.0.4\n\n### 0.1.5--2022.1.15\n\n- 新增服务器控制台指令,新增依赖rcon\n- 重新了数据库,不再使用json而是使用sql3\n- 改写了求生anne信息显示方式:如果单个数据以图片显示,如果多个数据以文字显示\n\n### 0.1.4--2022.1.9\n\n- 新增求生anne详情(看排名)\n- 所有的请求改为httpx\n- 更新了anne信息图片\n- 可选使用模拟谷歌浏览器来获取anne更多数据(~有点屎了,希望大佬救救~)\n\n### 0.1.3--2022.1.7\n\n- 新增绑定昵称和steamid\n- 新增可以艾特人查询anne成绩\n- 新增解绑信息\n\n### 0.1.2--2022.1.6\n\n- 新增支持图片输出\n- 新增查询anne服数据\n\n### 0.1.1--2022.1.5\n\n- 新增删除地图\n- 新增地图改名\n- 新增支持图片输出\n\n### 0.1.0--2022.1.4\n\n- 集中修复了Bug\n\n### 0.0.9--2022.1.4\n\n- 新增上传地图后,检测对比回复新地图名字\n- 修复中文名乱码问题\n\n### 0.0.8--2022.1.4\n\n- 支持vpk格式地图\n- 支持查看所有vpk格式文件\n\n### 0.0.6--2022.1.3\n\n- 修复了7z压缩包的方式,优化代码\n\n### 0.0.1--2022.1.3\n\n- 插件初次发布,可私聊添加地图\n\n</details>\n\n## ✨ 开发环境\nwin10 python3.9 <br>\nubuntu20.04 python3.10\n\n## 📖许可证GPL3.0\n\n This project is licensed under the GNU General Public License v3.0. See the LICENSE file for more information.\n\n\n<h2 id="ty">🌐感谢</h2>\n\n- 1、[修仙插件的数据库写法](https://github.com/s52047qwas/nonebot_plugin_xiuxian)\n- 2、~[自己写的求生之路查询库](https://github.com/Umamusume-Agnes-Digital/VSQ)~\n- 3、[可爱小Q的帮助(太好看了tql)](https://github.com/MeetWq/mybot)\n- 4、感谢petpet交流群各位大佬指点nonebot2事件处理api\n- 5、ArcPav改的暗♂黑风格菜单\n\n<h2 id="cx">服务器相关 </h2>\n\n目前插件会内置经腐竹同意的服的查询(未来将使用api)\n\n| 指令 | 服务器 | op | 数量 |\n|:-----:|:----:|:----:|:----:|\n| 云 | anne电信服云服(暂时不提供) | 东 | 24\n| 呆呆 | 呆呆的小窝 | 提莫大魔王 | 15\n| 橘 | 橘希实香的小窝 | 橘希实香 | 7\n\n如果需要上传自己的ip可以Pr、iss或者进qq群\n\n[pr修改这个文件](https://github.com/Umamusume-Agnes-Digital/nonebot_plugin_l4d2_server/blob/main/nonebot_plugin_l4d2_server/data/L4D2/l4d2.json)\n',
44
47
  'author': 'Agnes_Digital',
45
48
  'author_email': 'Z735803792@163.com',
46
49
  'maintainer': 'None',