nonebot-plugin-l4d2-server 0.5.1__py3-none-any.whl → 0.5.2__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 (48) hide show
  1. LICENSE +674 -674
  2. README.md +364 -359
  3. nonebot_plugin_l4d2_server/__init__.py +1 -1
  4. nonebot_plugin_l4d2_server/chrome.py +44 -44
  5. nonebot_plugin_l4d2_server/command.py +232 -232
  6. nonebot_plugin_l4d2_server/config.py +210 -210
  7. nonebot_plugin_l4d2_server/data/L4D2/image/template/anne.html +60 -60
  8. nonebot_plugin_l4d2_server/data/L4D2/image/template/fingerprint.svg +15 -15
  9. nonebot_plugin_l4d2_server/data/L4D2/image/template/help.html +233 -233
  10. nonebot_plugin_l4d2_server/data/L4D2/image/template/help_dack.html +231 -231
  11. nonebot_plugin_l4d2_server/data/L4D2/image/template/ip.html +48 -48
  12. nonebot_plugin_l4d2_server/data/L4D2/image/template/l.svg +9 -9
  13. nonebot_plugin_l4d2_server/l4d2_anne/__init__.py +251 -251
  14. nonebot_plugin_l4d2_server/l4d2_anne/analysis.py +51 -51
  15. nonebot_plugin_l4d2_server/l4d2_anne/anne_telecom.py +75 -75
  16. nonebot_plugin_l4d2_server/l4d2_anne/server.py +65 -65
  17. nonebot_plugin_l4d2_server/l4d2_anne/startand.py +17 -17
  18. nonebot_plugin_l4d2_server/l4d2_data/__init__.py +91 -91
  19. nonebot_plugin_l4d2_server/l4d2_data/config.py +17 -17
  20. nonebot_plugin_l4d2_server/l4d2_data/players.py +87 -87
  21. nonebot_plugin_l4d2_server/l4d2_data/serverip.py +32 -32
  22. nonebot_plugin_l4d2_server/l4d2_file/__init__.py +122 -122
  23. nonebot_plugin_l4d2_server/l4d2_file/ayromote.py +56 -56
  24. nonebot_plugin_l4d2_server/l4d2_file/remote.py +63 -63
  25. nonebot_plugin_l4d2_server/l4d2_image/__init__.py +103 -103
  26. nonebot_plugin_l4d2_server/l4d2_image/download.py +101 -101
  27. nonebot_plugin_l4d2_server/l4d2_image/htmlimg.py +32 -32
  28. nonebot_plugin_l4d2_server/l4d2_image/send_image_tool.py +32 -32
  29. nonebot_plugin_l4d2_server/l4d2_image/steam.py +83 -83
  30. nonebot_plugin_l4d2_server/l4d2_image/vtfs.py +40 -40
  31. nonebot_plugin_l4d2_server/l4d2_queries/__init__.py +114 -114
  32. nonebot_plugin_l4d2_server/l4d2_queries/api.py +43 -43
  33. nonebot_plugin_l4d2_server/l4d2_queries/ohter.py +35 -35
  34. nonebot_plugin_l4d2_server/l4d2_queries/qqgroup.py +288 -288
  35. nonebot_plugin_l4d2_server/l4d2_server/__init__.py +61 -61
  36. nonebot_plugin_l4d2_server/l4d2_server/rcon.py +28 -28
  37. nonebot_plugin_l4d2_server/l4d2_server/workshop.py +50 -50
  38. nonebot_plugin_l4d2_server/l4d2_web/web.py +234 -252
  39. nonebot_plugin_l4d2_server/l4d2_web/webUI.py +241 -245
  40. nonebot_plugin_l4d2_server/message.py +58 -58
  41. nonebot_plugin_l4d2_server/seach.py +33 -33
  42. nonebot_plugin_l4d2_server/txt_to_img.py +64 -64
  43. nonebot_plugin_l4d2_server/utils.py +272 -272
  44. {nonebot_plugin_l4d2_server-0.5.1.dist-info → nonebot_plugin_l4d2_server-0.5.2.dist-info}/LICENSE +674 -674
  45. {nonebot_plugin_l4d2_server-0.5.1.dist-info → nonebot_plugin_l4d2_server-0.5.2.dist-info}/METADATA +6 -1
  46. nonebot_plugin_l4d2_server-0.5.2.dist-info/RECORD +54 -0
  47. {nonebot_plugin_l4d2_server-0.5.1.dist-info → nonebot_plugin_l4d2_server-0.5.2.dist-info}/WHEEL +1 -1
  48. nonebot_plugin_l4d2_server-0.5.1.dist-info/RECORD +0 -54
@@ -1,63 +1,63 @@
1
- import asyncio
2
- import paramiko
3
-
4
- class SSHClient:
5
- def __init__(self, hostname, port, username, password):
6
- self._hostname = hostname
7
- self._port = port
8
- self._username = username
9
- self._password = password
10
- self._ssh = None
11
-
12
- async def connect(self):
13
- self._ssh = paramiko.SSHClient()
14
- self._ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
15
- await asyncio.get_event_loop().run_in_executor(None, self._ssh.connect, self._hostname, self._port, self._username, self._password)
16
-
17
- async def upload(self, local_file_path, remote_file_path):
18
- with paramiko.Transport((self._hostname, self._port)) as transport:
19
- await asyncio.get_event_loop().run_in_executor(None, transport.connect, None, self._username, self._password)
20
- sftp = paramiko.SFTPClient.from_transport(transport)
21
- await asyncio.get_event_loop().run_in_executor(None, sftp.put, local_file_path, remote_file_path)
22
-
23
-
24
- async def delete(self, remote_file_path):
25
- with paramiko.Transport((self._hostname, self._port)) as transport:
26
- await asyncio.get_event_loop().run_in_executor(None, transport.connect, None, self._username, self._password)
27
- sftp = paramiko.SFTPClient.from_transport(transport)
28
- await asyncio.get_event_loop().run_in_executor(None, sftp.remove, remote_file_path)
29
-
30
- async def read(self, remote_dir_path):
31
- with paramiko.Transport((self._hostname, self._port)) as transport:
32
- await asyncio.get_event_loop().run_in_executor(None, transport.connect, None, self._username, self._password)
33
- sftp = paramiko.SFTPClient.from_transport(transport)
34
- return await asyncio.get_event_loop().run_in_executor(None, sftp.listdir, remote_dir_path)
35
-
36
- async def close(self):
37
- if self._ssh is not None:
38
- self._ssh.close()
39
-
40
- # async def main():
41
- # ssh = SSHClient("example.com", 22, "username", "password")
42
- # await ssh.connect()
43
-
44
- # await ssh.upload("/path/to/local/file", "/path/to/remote/file")
45
- # await ssh.delete("/path/to/remote/file")
46
- # files = await ssh.list("/path/to/remote/directory")
47
- # print(files)
48
-
49
- # await ssh.close()
50
-
51
- # if __name__ == '__main__':
52
- # asyncio.run(main())
53
-
54
- async def remote(mode:str,host,user,password,local_path = '',port=22, remote_path = ''):
55
- """mode:upload、read、del"""
56
- client = SSHClient(host, port,user, password)
57
- if mode == 'upload':
58
- await client.upload(local_path, remote_path)
59
- elif mode == 'read':
60
- file = await client.read(remote_path)
61
- return file
62
- elif mode == 'del':
63
- await client.delete(remote_path)
1
+ import asyncio
2
+ import paramiko
3
+
4
+ class SSHClient:
5
+ def __init__(self, hostname, port, username, password):
6
+ self._hostname = hostname
7
+ self._port = port
8
+ self._username = username
9
+ self._password = password
10
+ self._ssh = None
11
+
12
+ async def connect(self):
13
+ self._ssh = paramiko.SSHClient()
14
+ self._ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
15
+ await asyncio.get_event_loop().run_in_executor(None, self._ssh.connect, self._hostname, self._port, self._username, self._password)
16
+
17
+ async def upload(self, local_file_path, remote_file_path):
18
+ with paramiko.Transport((self._hostname, self._port)) as transport:
19
+ await asyncio.get_event_loop().run_in_executor(None, transport.connect, None, self._username, self._password)
20
+ sftp = paramiko.SFTPClient.from_transport(transport)
21
+ await asyncio.get_event_loop().run_in_executor(None, sftp.put, local_file_path, remote_file_path)
22
+
23
+
24
+ async def delete(self, remote_file_path):
25
+ with paramiko.Transport((self._hostname, self._port)) as transport:
26
+ await asyncio.get_event_loop().run_in_executor(None, transport.connect, None, self._username, self._password)
27
+ sftp = paramiko.SFTPClient.from_transport(transport)
28
+ await asyncio.get_event_loop().run_in_executor(None, sftp.remove, remote_file_path)
29
+
30
+ async def read(self, remote_dir_path):
31
+ with paramiko.Transport((self._hostname, self._port)) as transport:
32
+ await asyncio.get_event_loop().run_in_executor(None, transport.connect, None, self._username, self._password)
33
+ sftp = paramiko.SFTPClient.from_transport(transport)
34
+ return await asyncio.get_event_loop().run_in_executor(None, sftp.listdir, remote_dir_path)
35
+
36
+ async def close(self):
37
+ if self._ssh is not None:
38
+ self._ssh.close()
39
+
40
+ # async def main():
41
+ # ssh = SSHClient("example.com", 22, "username", "password")
42
+ # await ssh.connect()
43
+
44
+ # await ssh.upload("/path/to/local/file", "/path/to/remote/file")
45
+ # await ssh.delete("/path/to/remote/file")
46
+ # files = await ssh.list("/path/to/remote/directory")
47
+ # print(files)
48
+
49
+ # await ssh.close()
50
+
51
+ # if __name__ == '__main__':
52
+ # asyncio.run(main())
53
+
54
+ async def remote(mode:str,host,user,password,local_path = '',port=22, remote_path = ''):
55
+ """mode:upload、read、del"""
56
+ client = SSHClient(host, port,user, password)
57
+ if mode == 'upload':
58
+ await client.upload(local_path, remote_path)
59
+ elif mode == 'read':
60
+ file = await client.read(remote_path)
61
+ return file
62
+ elif mode == 'del':
63
+ await client.delete(remote_path)
@@ -1,103 +1,103 @@
1
-
2
- from bs4 import BeautifulSoup
3
- from nonebot.log import logger
4
- from nonebot_plugin_htmlrender import html_to_pic
5
- from typing import List,Optional
6
- # from .htmlimg import dict_to_dict_img
7
- # from ..l4d2_anne.anne_telecom import ANNE_API
8
- from ..config import TEXT_PATH
9
- from .download import get_head_by_user_id_and_save
10
- from .send_image_tool import convert_img
11
- import jinja2
12
- from ..config import l4_config
13
- template_path = TEXT_PATH/"template"
14
-
15
- env = jinja2.Environment(
16
- loader=jinja2.FileSystemLoader(template_path), enable_async=True
17
- )
18
-
19
- async def out_png(usr_id,data_dict:dict):
20
- """使用html来生成图片"""
21
-
22
- with open((template_path/"anne.html"),"r", encoding="utf-8") as file:
23
- data_html = file.read()
24
- # content = template.render_async()
25
- soup = BeautifulSoup(data_html, 'html.parser')
26
- msg_dict = await dict_to_html(usr_id,data_dict,soup)
27
- template = env.get_template('anne.html')
28
- html = await template.render_async(data=msg_dict)
29
- pic = await html_to_pic(
30
- html,
31
- wait=0,
32
- viewport={"width": 1100, "height": 800},
33
- template_path=f"file://{template_path.absolute()}",)
34
- print(type(pic))
35
- return pic
36
-
37
-
38
- async def dict_to_html(usr_id,DETAIL_MAP:dict,soup:BeautifulSoup):
39
- """输入qq、字典,获取新的msg替换html"""
40
- DETAIL_right = {}
41
- DETAIL_right['name'] = DETAIL_MAP['Steam 名字:']
42
- DETAIL_right['Steam_ID'] = DETAIL_MAP['Steam ID:']
43
- DETAIL_right['play_time'] = DETAIL_MAP['游玩时间:']
44
- DETAIL_right['last_online'] = DETAIL_MAP['最后上线:']
45
- DETAIL_right['rank'] = DETAIL_MAP['排行:']
46
- DETAIL_right['points'] = DETAIL_MAP['分数:']
47
- DETAIL_right['point_min'] = DETAIL_MAP['每分钟获取分数:']
48
- DETAIL_right['killed'] = DETAIL_MAP['感染者消灭:']
49
- DETAIL_right['shut'] = DETAIL_MAP['爆头:']
50
- DETAIL_right['out'] = DETAIL_MAP['爆头率:']
51
- DETAIL_right['playtimes'] = DETAIL_MAP['游玩地图数量:']
52
- DETAIL_right['url'] = DETAIL_MAP['个人资料']
53
- DETAIL_right['one_msg'] = DETAIL_MAP['一言']
54
- DETAIL_right['last_one'] = DETAIL_MAP['救援关']
55
- # html_text = soup.prettify()
56
- # for key, value in DETAIL_right.items():
57
- # html_text = html_text.replace(key,value)
58
- # 头像
59
- temp = await get_head_by_user_id_and_save(usr_id)
60
- # temp = await get_head_steam_and_save(usr_id,DETAIL_right['url'])
61
- res = await convert_img(temp,is_base64=True)
62
- DETAIL_right['header'] = f"data:image/png;base64,{res}"
63
- data_list:List[dict] = [DETAIL_right]
64
- return data_list
65
-
66
-
67
- async def server_ip_pic(msg_list:List[dict]):
68
- """
69
- 输入一个字典列表,输出图片
70
- msg_dict:folder/name/map_/players/max_players/Players/[Name]
71
- """
72
- for server_info in msg_list:
73
- server_info['max_players'] = f"{server_info['players']}/{server_info['max_players']}"
74
- players_list = []
75
- if 'Players' in server_info:
76
- sorted_players = sorted(server_info['Players'], key=lambda x: x.get('Score', 0), reverse=True)[:4]
77
- for player_info in sorted_players:
78
- player_str = f"{player_info['name']} | {player_info['Duration']}"
79
- players_list.append(player_str)
80
- while len(players_list) < 4:
81
- players_list.append("")
82
- server_info['Players'] = players_list
83
- pic = await get_help_img(msg_list)
84
-
85
- return pic
86
-
87
-
88
- async def get_help_img(plugins: List[dict]) -> Optional[bytes]:
89
- try:
90
- if l4_config.l4_style == 'black':
91
- template = env.get_template("help_dack.html")
92
- else:
93
- template = env.get_template("help.html")
94
- content = await template.render_async(plugins=plugins)
95
- return await html_to_pic(
96
- content,
97
- wait=0,
98
- viewport={"width": 100, "height": 100},
99
- template_path=f"file://{template_path.absolute()}",
100
- )
101
- except Exception as e:
102
- logger.warning(f"Error in get_help_img: {e}")
103
- return None
1
+
2
+ from bs4 import BeautifulSoup
3
+ from nonebot.log import logger
4
+ from nonebot_plugin_htmlrender import html_to_pic
5
+ from typing import List,Optional
6
+ # from .htmlimg import dict_to_dict_img
7
+ # from ..l4d2_anne.anne_telecom import ANNE_API
8
+ from ..config import TEXT_PATH
9
+ from .download import get_head_by_user_id_and_save
10
+ from .send_image_tool import convert_img
11
+ import jinja2
12
+ from ..config import l4_config
13
+ template_path = TEXT_PATH/"template"
14
+
15
+ env = jinja2.Environment(
16
+ loader=jinja2.FileSystemLoader(template_path), enable_async=True
17
+ )
18
+
19
+ async def out_png(usr_id,data_dict:dict):
20
+ """使用html来生成图片"""
21
+
22
+ with open((template_path/"anne.html"),"r", encoding="utf-8") as file:
23
+ data_html = file.read()
24
+ # content = template.render_async()
25
+ soup = BeautifulSoup(data_html, 'html.parser')
26
+ msg_dict = await dict_to_html(usr_id,data_dict,soup)
27
+ template = env.get_template('anne.html')
28
+ html = await template.render_async(data=msg_dict)
29
+ pic = await html_to_pic(
30
+ html,
31
+ wait=0,
32
+ viewport={"width": 1100, "height": 800},
33
+ template_path=f"file://{template_path.absolute()}",)
34
+ print(type(pic))
35
+ return pic
36
+
37
+
38
+ async def dict_to_html(usr_id,DETAIL_MAP:dict,soup:BeautifulSoup):
39
+ """输入qq、字典,获取新的msg替换html"""
40
+ DETAIL_right = {}
41
+ DETAIL_right['name'] = DETAIL_MAP['Steam 名字:']
42
+ DETAIL_right['Steam_ID'] = DETAIL_MAP['Steam ID:']
43
+ DETAIL_right['play_time'] = DETAIL_MAP['游玩时间:']
44
+ DETAIL_right['last_online'] = DETAIL_MAP['最后上线:']
45
+ DETAIL_right['rank'] = DETAIL_MAP['排行:']
46
+ DETAIL_right['points'] = DETAIL_MAP['分数:']
47
+ DETAIL_right['point_min'] = DETAIL_MAP['每分钟获取分数:']
48
+ DETAIL_right['killed'] = DETAIL_MAP['感染者消灭:']
49
+ DETAIL_right['shut'] = DETAIL_MAP['爆头:']
50
+ DETAIL_right['out'] = DETAIL_MAP['爆头率:']
51
+ DETAIL_right['playtimes'] = DETAIL_MAP['游玩地图数量:']
52
+ DETAIL_right['url'] = DETAIL_MAP['个人资料']
53
+ DETAIL_right['one_msg'] = DETAIL_MAP['一言']
54
+ DETAIL_right['last_one'] = DETAIL_MAP['救援关']
55
+ # html_text = soup.prettify()
56
+ # for key, value in DETAIL_right.items():
57
+ # html_text = html_text.replace(key,value)
58
+ # 头像
59
+ temp = await get_head_by_user_id_and_save(usr_id)
60
+ # temp = await get_head_steam_and_save(usr_id,DETAIL_right['url'])
61
+ res = await convert_img(temp,is_base64=True)
62
+ DETAIL_right['header'] = f"data:image/png;base64,{res}"
63
+ data_list:List[dict] = [DETAIL_right]
64
+ return data_list
65
+
66
+
67
+ async def server_ip_pic(msg_list:List[dict]):
68
+ """
69
+ 输入一个字典列表,输出图片
70
+ msg_dict:folder/name/map_/players/max_players/Players/[Name]
71
+ """
72
+ for server_info in msg_list:
73
+ server_info['max_players'] = f"{server_info['players']}/{server_info['max_players']}"
74
+ players_list = []
75
+ if 'Players' in server_info:
76
+ sorted_players = sorted(server_info['Players'], key=lambda x: x.get('Score', 0), reverse=True)[:4]
77
+ for player_info in sorted_players:
78
+ player_str = f"{player_info['name']} | {player_info['Duration']}"
79
+ players_list.append(player_str)
80
+ while len(players_list) < 4:
81
+ players_list.append("")
82
+ server_info['Players'] = players_list
83
+ pic = await get_help_img(msg_list)
84
+
85
+ return pic
86
+
87
+
88
+ async def get_help_img(plugins: List[dict]) -> Optional[bytes]:
89
+ try:
90
+ if l4_config.l4_style == 'black':
91
+ template = env.get_template("help_dack.html")
92
+ else:
93
+ template = env.get_template("help.html")
94
+ content = await template.render_async(plugins=plugins)
95
+ return await html_to_pic(
96
+ content,
97
+ wait=0,
98
+ viewport={"width": 100, "height": 100},
99
+ template_path=f"file://{template_path.absolute()}",
100
+ )
101
+ except Exception as e:
102
+ logger.warning(f"Error in get_help_img: {e}")
103
+ return None
@@ -1,102 +1,102 @@
1
- import httpx
2
- from nonebot.log import logger
3
- import asyncio
4
- import hashlib
5
- import os
6
- import random
7
- from PIL import Image,ImageDraw
8
- import io
9
- from ..config import PLAYERSDATA,TEXT_PATH
10
- # from .steam import web_player
11
-
12
- async def download_url(url: str) -> bytes:
13
- async with httpx.AsyncClient() as client:
14
- for i in range(3):
15
- try:
16
- resp = await client.get(url, timeout=20)
17
- resp.raise_for_status()
18
- return resp.content
19
- except Exception as e:
20
- logger.warning(f"Error downloading {url}, retry {i}/3: {e}")
21
- await asyncio.sleep(3)
22
- raise Exception(f"{url} 下载失败!")
23
-
24
- async def download_head(user_id: str) -> bytes:
25
- url = f"http://q1.qlogo.cn/g?b=qq&nk={user_id}&s=640"
26
- data = await download_url(url)
27
- if hashlib.md5(data).hexdigest() == "acef72340ac0e914090bd35799f5594e":
28
- url = f"http://q1.qlogo.cn/g?b=qq&nk={user_id}&s=100"
29
- data = await download_url(url)
30
- return data
31
-
32
-
33
- def square_to_circle(im:Image):
34
- """im是正方形,变圆形"""
35
- size = im.size
36
- mask = Image.new('L', size, 0)
37
- draw = ImageDraw.Draw(mask)
38
- draw.ellipse((0, 0) + size, fill=255)
39
- # 将遮罩层应用到图像上
40
- im.putalpha(mask)
41
- return im
42
-
43
- async def get_head_by_user_id_and_save(user_id):
44
- """qq转头像"""
45
- user_id = str(user_id)
46
-
47
- USER_HEAD_PATH = PLAYERSDATA / user_id / 'HEAD.png'
48
- DEFAULT_HEADER_PATH = TEXT_PATH / "header"
49
- DEFAULT_HEAD_PATH = TEXT_PATH / "head"
50
- DEFAULT_HEADER = DEFAULT_HEADER_PATH /random.choice(os.listdir(DEFAULT_HEADER_PATH))
51
- DEFAULT_HEAD = DEFAULT_HEAD_PATH /random.choice(os.listdir(DEFAULT_HEAD_PATH))
52
- ## im头像 im2头像框 im3合成
53
- if os.path.exists(USER_HEAD_PATH):
54
- logger.info("使用本地头像")
55
- im = Image.open(USER_HEAD_PATH).resize((280, 280)).convert("RGBA")
56
- else:
57
- if user_id == "1145149191810":
58
- logger.info("使用默认头像")
59
- im = Image.open(DEFAULT_HEADER).resize((280, 280)).convert("RGBA")
60
- else:
61
- try:
62
- logger.info("正在下载头像")
63
- image_bytes = await download_head(user_id)
64
- im = Image.open(io.BytesIO(image_bytes)).resize((280, 280)).convert("RGBA")
65
- if not os.path.exists(PLAYERSDATA / user_id):#用户文件夹不存在
66
- os.makedirs(PLAYERSDATA / user_id)
67
- im.save(USER_HEAD_PATH, "PNG")
68
- except:
69
- logger.error("获取失败")
70
- im2 = Image.open(DEFAULT_HEAD).resize((450,450)).convert("RGBA")
71
- im3 = Image.new("RGBA", im2.size, (255,255,255,0))
72
- r, g, b, a1 = im.split()
73
- r, g, b, a2 = im2.split()
74
- im = square_to_circle(im)
75
- im3.paste(im,(75,75),mask=a1)
76
- im3.paste(im2,mask=a2)
77
- return im3
78
-
79
- # async def get_head_steam_and_save(user_id:str,urls):
80
- # """保存steam头像"""
81
- # USER_HEAD_PATH = PLAYERSDATA / str(user_id) / 'HEAD.png'
82
- # # DEFAULT_HEAD_PATH = TEXT_PATH / "template/player.jpg"
83
- # ## im头像 im2头像框 im3合成
84
- # if os.path.exists(USER_HEAD_PATH):
85
- # logger.info("使用本地头像")
86
- # im = Image.open(USER_HEAD_PATH).resize((280, 280)).convert("RGBA")
87
- # else:
88
- # # if user_id == "1145149191810":
89
- # # logger.info("使用默认头像")
90
- # # im = Image.open(DEFAULT_HEAD_PATH).resize((300, 300)).convert("RGBA")
91
- # # else:
92
- # try:
93
- # logger.info("正在下载头像")
94
- # image_bytes = await web_player(urls)
95
- # im = Image.open(io.BytesIO(image_bytes)).resize((280, 280)).convert("RGBA")
96
- # if not os.path.exists(PLAYERSDATA / user_id):#用户文件夹不存在
97
- # os.makedirs(PLAYERSDATA / user_id)
98
- # im.save(USER_HEAD_PATH, "PNG")
99
- # except TimeoutError:
100
- # logger.error("获取失败")
101
- # return im
1
+ import httpx
2
+ from nonebot.log import logger
3
+ import asyncio
4
+ import hashlib
5
+ import os
6
+ import random
7
+ from PIL import Image,ImageDraw
8
+ import io
9
+ from ..config import PLAYERSDATA,TEXT_PATH
10
+ # from .steam import web_player
11
+
12
+ async def download_url(url: str) -> bytes:
13
+ async with httpx.AsyncClient() as client:
14
+ for i in range(3):
15
+ try:
16
+ resp = await client.get(url, timeout=20)
17
+ resp.raise_for_status()
18
+ return resp.content
19
+ except Exception as e:
20
+ logger.warning(f"Error downloading {url}, retry {i}/3: {e}")
21
+ await asyncio.sleep(3)
22
+ raise Exception(f"{url} 下载失败!")
23
+
24
+ async def download_head(user_id: str) -> bytes:
25
+ url = f"http://q1.qlogo.cn/g?b=qq&nk={user_id}&s=640"
26
+ data = await download_url(url)
27
+ if hashlib.md5(data).hexdigest() == "acef72340ac0e914090bd35799f5594e":
28
+ url = f"http://q1.qlogo.cn/g?b=qq&nk={user_id}&s=100"
29
+ data = await download_url(url)
30
+ return data
31
+
32
+
33
+ def square_to_circle(im:Image):
34
+ """im是正方形,变圆形"""
35
+ size = im.size
36
+ mask = Image.new('L', size, 0)
37
+ draw = ImageDraw.Draw(mask)
38
+ draw.ellipse((0, 0) + size, fill=255)
39
+ # 将遮罩层应用到图像上
40
+ im.putalpha(mask)
41
+ return im
42
+
43
+ async def get_head_by_user_id_and_save(user_id):
44
+ """qq转头像"""
45
+ user_id = str(user_id)
46
+
47
+ USER_HEAD_PATH = PLAYERSDATA / user_id / 'HEAD.png'
48
+ DEFAULT_HEADER_PATH = TEXT_PATH / "header"
49
+ DEFAULT_HEAD_PATH = TEXT_PATH / "head"
50
+ DEFAULT_HEADER = DEFAULT_HEADER_PATH /random.choice(os.listdir(DEFAULT_HEADER_PATH))
51
+ DEFAULT_HEAD = DEFAULT_HEAD_PATH /random.choice(os.listdir(DEFAULT_HEAD_PATH))
52
+ ## im头像 im2头像框 im3合成
53
+ if os.path.exists(USER_HEAD_PATH):
54
+ logger.info("使用本地头像")
55
+ im = Image.open(USER_HEAD_PATH).resize((280, 280)).convert("RGBA")
56
+ else:
57
+ if user_id == "1145149191810":
58
+ logger.info("使用默认头像")
59
+ im = Image.open(DEFAULT_HEADER).resize((280, 280)).convert("RGBA")
60
+ else:
61
+ try:
62
+ logger.info("正在下载头像")
63
+ image_bytes = await download_head(user_id)
64
+ im = Image.open(io.BytesIO(image_bytes)).resize((280, 280)).convert("RGBA")
65
+ if not os.path.exists(PLAYERSDATA / user_id):#用户文件夹不存在
66
+ os.makedirs(PLAYERSDATA / user_id)
67
+ im.save(USER_HEAD_PATH, "PNG")
68
+ except:
69
+ logger.error("获取失败")
70
+ im2 = Image.open(DEFAULT_HEAD).resize((450,450)).convert("RGBA")
71
+ im3 = Image.new("RGBA", im2.size, (255,255,255,0))
72
+ r, g, b, a1 = im.split()
73
+ r, g, b, a2 = im2.split()
74
+ im = square_to_circle(im)
75
+ im3.paste(im,(75,75),mask=a1)
76
+ im3.paste(im2,mask=a2)
77
+ return im3
78
+
79
+ # async def get_head_steam_and_save(user_id:str,urls):
80
+ # """保存steam头像"""
81
+ # USER_HEAD_PATH = PLAYERSDATA / str(user_id) / 'HEAD.png'
82
+ # # DEFAULT_HEAD_PATH = TEXT_PATH / "template/player.jpg"
83
+ # ## im头像 im2头像框 im3合成
84
+ # if os.path.exists(USER_HEAD_PATH):
85
+ # logger.info("使用本地头像")
86
+ # im = Image.open(USER_HEAD_PATH).resize((280, 280)).convert("RGBA")
87
+ # else:
88
+ # # if user_id == "1145149191810":
89
+ # # logger.info("使用默认头像")
90
+ # # im = Image.open(DEFAULT_HEAD_PATH).resize((300, 300)).convert("RGBA")
91
+ # # else:
92
+ # try:
93
+ # logger.info("正在下载头像")
94
+ # image_bytes = await web_player(urls)
95
+ # im = Image.open(io.BytesIO(image_bytes)).resize((280, 280)).convert("RGBA")
96
+ # if not os.path.exists(PLAYERSDATA / user_id):#用户文件夹不存在
97
+ # os.makedirs(PLAYERSDATA / user_id)
98
+ # im.save(USER_HEAD_PATH, "PNG")
99
+ # except TimeoutError:
100
+ # logger.error("获取失败")
101
+ # return im
102
102
 
@@ -1,33 +1,33 @@
1
-
2
-
3
- class DATA_PANDS:
4
- async def __init__(
5
- self,
6
- data_dict:dict
7
- ) -> None:
8
- self.new_data = {[]}
9
- self.data_dict = data_dict
10
- for key,value in data_dict.items():
11
- self.dict_pan(key)
12
-
13
-
14
-
15
- async def dict_pan(self,key):
16
- """dict转化为图像所需要的dict量化"""
17
- # 删除不必要是数据
18
- if key in ['刷特模式']:
19
- for item in self.data_dict[key]:
20
- if item == '固定刷特':
21
- self.data_dict.pop(item)
22
- elif key in ['游戏模式']:
23
- for item in self.data_dict[key]:
24
- if item in self.new_data:
25
- self.new_data[item] += 1
26
- else:
27
- self.new_data[item] = 1
28
- elif key in ['特感数量','刷新间隔']:
29
- for item in self.data_dict[key]:
30
- if item in self.new_data:
31
- self.new_data[item] += 1
32
- else:
1
+
2
+
3
+ class DATA_PANDS:
4
+ async def __init__(
5
+ self,
6
+ data_dict:dict
7
+ ) -> None:
8
+ self.new_data = {[]}
9
+ self.data_dict = data_dict
10
+ for key,value in data_dict.items():
11
+ self.dict_pan(key)
12
+
13
+
14
+
15
+ async def dict_pan(self,key):
16
+ """dict转化为图像所需要的dict量化"""
17
+ # 删除不必要是数据
18
+ if key in ['刷特模式']:
19
+ for item in self.data_dict[key]:
20
+ if item == '固定刷特':
21
+ self.data_dict.pop(item)
22
+ elif key in ['游戏模式']:
23
+ for item in self.data_dict[key]:
24
+ if item in self.new_data:
25
+ self.new_data[item] += 1
26
+ else:
27
+ self.new_data[item] = 1
28
+ elif key in ['特感数量','刷新间隔']:
29
+ for item in self.data_dict[key]:
30
+ if item in self.new_data:
31
+ self.new_data[item] += 1
32
+ else:
33
33
  self.new_data[item] = 1