endstone-qqsync-lite-plugin 0.0.1__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 (62) hide show
  1. endstone_qqsync_lite_plugin/__init__.py +3 -0
  2. endstone_qqsync_lite_plugin/client.py +124 -0
  3. endstone_qqsync_lite_plugin/config_manager.py +64 -0
  4. endstone_qqsync_lite_plugin/handlers.py +118 -0
  5. endstone_qqsync_lite_plugin/lib/websockets/__init__.py +236 -0
  6. endstone_qqsync_lite_plugin/lib/websockets/__main__.py +5 -0
  7. endstone_qqsync_lite_plugin/lib/websockets/asyncio/__init__.py +0 -0
  8. endstone_qqsync_lite_plugin/lib/websockets/asyncio/async_timeout.py +282 -0
  9. endstone_qqsync_lite_plugin/lib/websockets/asyncio/client.py +820 -0
  10. endstone_qqsync_lite_plugin/lib/websockets/asyncio/compatibility.py +30 -0
  11. endstone_qqsync_lite_plugin/lib/websockets/asyncio/connection.py +1237 -0
  12. endstone_qqsync_lite_plugin/lib/websockets/asyncio/messages.py +314 -0
  13. endstone_qqsync_lite_plugin/lib/websockets/asyncio/router.py +198 -0
  14. endstone_qqsync_lite_plugin/lib/websockets/asyncio/server.py +981 -0
  15. endstone_qqsync_lite_plugin/lib/websockets/auth.py +18 -0
  16. endstone_qqsync_lite_plugin/lib/websockets/cli.py +178 -0
  17. endstone_qqsync_lite_plugin/lib/websockets/client.py +389 -0
  18. endstone_qqsync_lite_plugin/lib/websockets/connection.py +12 -0
  19. endstone_qqsync_lite_plugin/lib/websockets/datastructures.py +187 -0
  20. endstone_qqsync_lite_plugin/lib/websockets/exceptions.py +473 -0
  21. endstone_qqsync_lite_plugin/lib/websockets/extensions/__init__.py +4 -0
  22. endstone_qqsync_lite_plugin/lib/websockets/extensions/base.py +123 -0
  23. endstone_qqsync_lite_plugin/lib/websockets/extensions/permessage_deflate.py +697 -0
  24. endstone_qqsync_lite_plugin/lib/websockets/frames.py +430 -0
  25. endstone_qqsync_lite_plugin/lib/websockets/headers.py +586 -0
  26. endstone_qqsync_lite_plugin/lib/websockets/http.py +20 -0
  27. endstone_qqsync_lite_plugin/lib/websockets/http11.py +427 -0
  28. endstone_qqsync_lite_plugin/lib/websockets/imports.py +100 -0
  29. endstone_qqsync_lite_plugin/lib/websockets/legacy/__init__.py +11 -0
  30. endstone_qqsync_lite_plugin/lib/websockets/legacy/auth.py +190 -0
  31. endstone_qqsync_lite_plugin/lib/websockets/legacy/client.py +705 -0
  32. endstone_qqsync_lite_plugin/lib/websockets/legacy/exceptions.py +71 -0
  33. endstone_qqsync_lite_plugin/lib/websockets/legacy/framing.py +225 -0
  34. endstone_qqsync_lite_plugin/lib/websockets/legacy/handshake.py +158 -0
  35. endstone_qqsync_lite_plugin/lib/websockets/legacy/http.py +201 -0
  36. endstone_qqsync_lite_plugin/lib/websockets/legacy/protocol.py +1641 -0
  37. endstone_qqsync_lite_plugin/lib/websockets/legacy/server.py +1191 -0
  38. endstone_qqsync_lite_plugin/lib/websockets/protocol.py +758 -0
  39. endstone_qqsync_lite_plugin/lib/websockets/py.typed +0 -0
  40. endstone_qqsync_lite_plugin/lib/websockets/server.py +587 -0
  41. endstone_qqsync_lite_plugin/lib/websockets/speedups.c +222 -0
  42. endstone_qqsync_lite_plugin/lib/websockets/speedups.cpython-312-x86_64-linux-gnu.so +0 -0
  43. endstone_qqsync_lite_plugin/lib/websockets/speedups.pyi +1 -0
  44. endstone_qqsync_lite_plugin/lib/websockets/streams.py +151 -0
  45. endstone_qqsync_lite_plugin/lib/websockets/sync/__init__.py +0 -0
  46. endstone_qqsync_lite_plugin/lib/websockets/sync/client.py +648 -0
  47. endstone_qqsync_lite_plugin/lib/websockets/sync/connection.py +1072 -0
  48. endstone_qqsync_lite_plugin/lib/websockets/sync/messages.py +345 -0
  49. endstone_qqsync_lite_plugin/lib/websockets/sync/router.py +192 -0
  50. endstone_qqsync_lite_plugin/lib/websockets/sync/server.py +763 -0
  51. endstone_qqsync_lite_plugin/lib/websockets/sync/utils.py +45 -0
  52. endstone_qqsync_lite_plugin/lib/websockets/typing.py +74 -0
  53. endstone_qqsync_lite_plugin/lib/websockets/uri.py +225 -0
  54. endstone_qqsync_lite_plugin/lib/websockets/utils.py +51 -0
  55. endstone_qqsync_lite_plugin/lib/websockets/version.py +92 -0
  56. endstone_qqsync_lite_plugin/qqsync_lite_plugin.py +102 -0
  57. endstone_qqsync_lite_plugin/utils/__init__.py +1 -0
  58. endstone_qqsync_lite_plugin/utils/imports.py +22 -0
  59. endstone_qqsync_lite_plugin-0.0.1.dist-info/METADATA +34 -0
  60. endstone_qqsync_lite_plugin-0.0.1.dist-info/RECORD +62 -0
  61. endstone_qqsync_lite_plugin-0.0.1.dist-info/WHEEL +4 -0
  62. endstone_qqsync_lite_plugin-0.0.1.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,3 @@
1
+ from endstone_qqsync_lite_plugin.qqsync_lite_plugin import qqsync
2
+
3
+ __all__ = ["qqsync"]
@@ -0,0 +1,124 @@
1
+ import asyncio
2
+ import json
3
+ from typing import Optional
4
+ from .utils.imports import import_websockets
5
+
6
+ websockets = import_websockets()
7
+
8
+ class WebSocketClient:
9
+ """WebSocket 客户端,用于与 OneBot WS 交互"""
10
+
11
+ def __init__(self, plugin):
12
+ self.plugin = plugin
13
+ self.logger = plugin.logger
14
+ self.ws = None
15
+ self._running = False
16
+
17
+ async def connect_forever(self):
18
+ """保持与 OneBot WS 服务的连接"""
19
+ if self._running:
20
+ return
21
+ self._running = True
22
+
23
+ napcat_ws = self.plugin.config_manager.get("napcat_ws")
24
+ access_token = self.plugin.config_manager.get("access_token")
25
+ headers = {"Authorization": f"Bearer {access_token}"} if access_token else {}
26
+
27
+ self.logger.info(f"正在连接 NapCat WS 地址: {napcat_ws}")
28
+ delay = 2
29
+
30
+ while self._running:
31
+ try:
32
+ async with websockets.connect(
33
+ napcat_ws,
34
+ additional_headers=headers,
35
+ ping_interval=20,
36
+ ping_timeout=10,
37
+ close_timeout=10
38
+ ) as websocket:
39
+ self.ws = websocket
40
+ self.plugin._current_ws = websocket
41
+ delay = 2 # 连接成功后重置重连延迟
42
+ self.logger.info("已成功连接 NapCat WS")
43
+
44
+ # 发送服务器启动通知
45
+ if self.plugin._send_startup_message:
46
+ if self.plugin.config_manager.get("enable_game_to_qq", True):
47
+ await self.send_group_msg_to_all("[QQSync-Lite] 服务器已启动!")
48
+ self.plugin._send_startup_message = False
49
+
50
+ # 消息接收主循环
51
+ async for message in websocket:
52
+ try:
53
+ data = json.loads(message)
54
+ await self._handle_message(data)
55
+ except Exception as e:
56
+ self.logger.error(f"处理收到的消息时出错: {e}")
57
+ except Exception as e:
58
+ self.ws = None
59
+ self.plugin._current_ws = None
60
+ if self._running:
61
+ self.logger.warning(f"NapCat WS 连接异常或断开: {e},将在 {delay} 秒后重试...")
62
+ await asyncio.sleep(delay)
63
+ delay = min(30, delay * 1.5)
64
+ else:
65
+ break
66
+
67
+ self.logger.info("NapCat WS 客户端已停止运行")
68
+
69
+ async def _handle_message(self, data: dict):
70
+ """处理来自 OneBot 协议的消息"""
71
+ post_type = data.get("post_type")
72
+ if post_type != "message":
73
+ return
74
+
75
+ message_type = data.get("message_type")
76
+ if message_type != "group":
77
+ return
78
+
79
+ group_id = data.get("group_id")
80
+ target_groups = self.plugin.config_manager.get("target_groups", [])
81
+ target_groups = [int(gid) for gid in target_groups]
82
+ if group_id not in target_groups:
83
+ return
84
+
85
+ raw_message = data.get("raw_message", "")
86
+ sender = data.get("sender", {})
87
+ nickname = sender.get("nickname", "未知")
88
+ card = sender.get("card", "")
89
+ display_name = card if card else nickname
90
+
91
+ # 转发到游戏内
92
+ if self.plugin.config_manager.get("enable_qq_to_game", True):
93
+ from .handlers import handle_qq_message
94
+ await handle_qq_message(self.plugin, group_id, display_name, raw_message)
95
+
96
+ async def send_group_msg_to_all(self, text: str):
97
+ """广播群消息到所有配置的 QQ 群"""
98
+ if not self.ws or self.ws.state != 1:
99
+ return
100
+
101
+ target_groups = self.plugin.config_manager.get("target_groups", [])
102
+ target_groups = [int(gid) for gid in target_groups]
103
+
104
+ for group_id in target_groups:
105
+ try:
106
+ payload = {
107
+ "action": "send_group_msg",
108
+ "params": {
109
+ "group_id": group_id,
110
+ "message": text
111
+ }
112
+ }
113
+ await self.ws.send(json.dumps(payload))
114
+ except Exception as e:
115
+ self.logger.error(f"发送群消息到群 {group_id} 失败: {e}")
116
+
117
+ def stop(self):
118
+ """停止客户端并关闭连接"""
119
+ self._running = False
120
+ if self.ws:
121
+ if self.plugin._loop and self.plugin._loop.is_running():
122
+ asyncio.run_coroutine_threadsafe(self.ws.close(), self.plugin._loop)
123
+ self.ws = None
124
+ self.plugin._current_ws = None
@@ -0,0 +1,64 @@
1
+ import json
2
+ from pathlib import Path
3
+ from typing import Any, Dict
4
+
5
+ class ConfigManager:
6
+ """配置管理器"""
7
+ def __init__(self, data_folder: Path, logger):
8
+ self.data_folder = data_folder
9
+ self.logger = logger
10
+ self.config_file = data_folder / "config.json"
11
+ self._config: Dict[str, Any] = {}
12
+
13
+ self.default_config = {
14
+ "napcat_ws": "ws://127.0.0.1:3001",
15
+ "access_token": "",
16
+ "target_groups": [12345678],
17
+ "group_names": {},
18
+ "enable_qq_to_game": True,
19
+ "enable_game_to_qq": True,
20
+ "enable_join_quit_msg": True,
21
+ "enable_death_msg": True,
22
+ "api_qq_enable": False
23
+ }
24
+ self._load_config()
25
+
26
+ def _load_config(self):
27
+ """加载配置文件,若不存在则写入默认配置"""
28
+ if not self.config_file.exists():
29
+ self.config_file.parent.mkdir(parents=True, exist_ok=True)
30
+ try:
31
+ with open(self.config_file, "w", encoding="utf-8") as f:
32
+ json.dump(self.default_config, f, indent=2, ensure_ascii=False)
33
+ self.logger.info("已创建默认配置文件 config.json")
34
+ except Exception as e:
35
+ self.logger.error(f"创建默认配置文件失败: {e}")
36
+
37
+ try:
38
+ with open(self.config_file, "r", encoding="utf-8") as f:
39
+ self._config = json.load(f)
40
+ except Exception as e:
41
+ self.logger.error(f"读取配置文件失败: {e}")
42
+ self._config = self.default_config.copy()
43
+
44
+ # 合并缺少的默认配置项
45
+ updated = False
46
+ for k, v in self.default_config.items():
47
+ if k not in self._config:
48
+ self._config[k] = v
49
+ updated = True
50
+
51
+ if updated:
52
+ self.save_config()
53
+
54
+ def get(self, key: str, default: Any = None) -> Any:
55
+ """获取配置项"""
56
+ return self._config.get(key, default)
57
+
58
+ def save_config(self):
59
+ """保存当前配置到文件"""
60
+ try:
61
+ with open(self.config_file, "w", encoding="utf-8") as f:
62
+ json.dump(self._config, f, indent=2, ensure_ascii=False)
63
+ except Exception as e:
64
+ self.logger.error(f"保存配置文件失败: {e}")
@@ -0,0 +1,118 @@
1
+ import re
2
+ from endstone.event import event_handler, PlayerChatEvent, PlayerJoinEvent, PlayerQuitEvent, PlayerDeathEvent
3
+ from endstone import ColorFormat
4
+
5
+ # 常用 emoji 字典映射
6
+ EMOJI_MAP = {
7
+ '😀': '[笑脸]', '😁': '[开心]', '😂': '[笑哭]', '🤣': '[大笑]', '😃': '[微笑]',
8
+ '😄': '[开心]', '😅': '[汗笑]', '👍': '[赞]', '👎': '[踩]', '👌': '[OK]',
9
+ '🔥': '[火]', '💯': '[100分]', '❤': '[红心]', '💔': '[心碎]'
10
+ }
11
+
12
+ def clean_and_parse_cq(text: str) -> str:
13
+ """清理和解析 QQ 消息中的 CQ 码和 Emoji"""
14
+ if not text:
15
+ return ""
16
+
17
+ # 替换已知 emoji
18
+ for emoji, desc in EMOJI_MAP.items():
19
+ text = text.replace(emoji, desc)
20
+
21
+ # 过滤其他未映射 emoji 区段
22
+ emoji_pattern = re.compile(
23
+ r'[\U0001F600-\U0001F64F\U0001F300-\U0001F5FF\U0001F680-\U0001F6FF\U00002600-\U000026FF\U00002700-\U000027BF]+'
24
+ )
25
+ text = emoji_pattern.sub('[表情]', text)
26
+
27
+ # 解析 CQ 码
28
+ def replace_cq(match):
29
+ cq_type = match.group(1)
30
+ cq_map = {
31
+ "image": "[图片]", "video": "[视频]", "record": "[语音]",
32
+ "face": "[表情]", "reply": "[回复]", "forward": "[转发]",
33
+ "file": "[文件]", "share": "[分享]", "location": "[位置]"
34
+ }
35
+ if cq_type in cq_map:
36
+ return cq_map[cq_type]
37
+ elif cq_type == "at":
38
+ params = match.group(2) or ""
39
+ if "qq=all" in params:
40
+ return "@全体成员"
41
+ else:
42
+ qq_match = re.search(r'qq=(\d+)', params)
43
+ return f"@{qq_match.group(1)}" if qq_match else "@某人"
44
+ return "[非文本]"
45
+
46
+ cq_pattern = r'\[CQ:([^,\]]+)(?:,([^\]]*))?\]'
47
+ text = re.sub(cq_pattern, replace_cq, text)
48
+
49
+ # 清理控制字符
50
+ text = re.sub(r'[\x00-\x1f\x7f-\x9f]', '', text)
51
+ return text.strip()
52
+
53
+ async def handle_qq_message(plugin, group_id: int, sender_name: str, raw_message: str):
54
+ """处理并格式化 QQ 消息转发到游戏"""
55
+ parsed_msg = clean_and_parse_cq(raw_message)
56
+ if not parsed_msg:
57
+ return
58
+
59
+ group_names = plugin.config_manager.get("group_names", {})
60
+ group_name = group_names.get(str(group_id), "")
61
+
62
+ if group_name:
63
+ game_message = f"{ColorFormat.GREEN}[QQ群] {ColorFormat.AQUA}[{group_name}] {sender_name}: {parsed_msg}{ColorFormat.RESET}"
64
+ else:
65
+ game_message = f"{ColorFormat.GREEN}[QQ群] {ColorFormat.AQUA}{sender_name}: {parsed_msg}{ColorFormat.RESET}"
66
+
67
+ plugin.logger.info(f"[QQ群->游戏] {sender_name}: {parsed_msg}")
68
+
69
+ # 主线程安全广播
70
+ def send():
71
+ for player in plugin.server.online_players:
72
+ player.send_message(game_message)
73
+
74
+ plugin.server.scheduler.run_task(plugin, send, delay=1)
75
+
76
+
77
+ class EventHandlers:
78
+ """游戏事件监听器"""
79
+ def __init__(self, plugin):
80
+ self.plugin = plugin
81
+
82
+ @event_handler
83
+ def on_player_chat(self, event: PlayerChatEvent):
84
+ """同步游戏内聊天消息到 QQ 群"""
85
+ if event.message.startswith('/'):
86
+ return
87
+
88
+ if self.plugin.config_manager.get("enable_game_to_qq", True):
89
+ chat_msg = f"{event.player.name}: {event.message}"
90
+ self.plugin.send_to_qq(chat_msg)
91
+
92
+ @event_handler
93
+ def on_player_join(self, event: PlayerJoinEvent):
94
+ """同步玩家加入提示到 QQ 群"""
95
+ if (self.plugin.config_manager.get("enable_game_to_qq", True) and
96
+ self.plugin.config_manager.get("enable_join_quit_msg", True)):
97
+ join_msg = f"[+] 玩家 {event.player.name} 加入了游戏"
98
+ self.plugin.send_to_qq(join_msg)
99
+
100
+ @event_handler
101
+ def on_player_quit(self, event: PlayerQuitEvent):
102
+ """同步玩家离开提示到 QQ 群"""
103
+ if (self.plugin.config_manager.get("enable_game_to_qq", True) and
104
+ self.plugin.config_manager.get("enable_join_quit_msg", True)):
105
+ quit_msg = f"[-] 玩家 {event.player.name} 离开了游戏"
106
+ self.plugin.send_to_qq(quit_msg)
107
+
108
+ @event_handler
109
+ def on_player_death(self, event: PlayerDeathEvent):
110
+ """同步玩家死亡提示到 QQ 群"""
111
+ if (self.plugin.config_manager.get("enable_game_to_qq", True) and
112
+ self.plugin.config_manager.get("enable_death_msg", True)):
113
+ try:
114
+ language = event.player.server.language
115
+ death_msg = language.translate(event.death_message, locale="zh_CN")
116
+ self.plugin.send_to_qq(death_msg)
117
+ except Exception as e:
118
+ self.plugin.logger.error(f"处理玩家死亡消息发送失败: {e}")
@@ -0,0 +1,236 @@
1
+ from __future__ import annotations
2
+
3
+ # Importing the typing module would conflict with websockets.typing.
4
+ from typing import TYPE_CHECKING
5
+
6
+ from .imports import lazy_import
7
+ from .version import version as __version__ # noqa: F401
8
+
9
+
10
+ __all__ = [
11
+ # .asyncio.client
12
+ "connect",
13
+ "unix_connect",
14
+ "ClientConnection",
15
+ # .asyncio.router
16
+ "route",
17
+ "unix_route",
18
+ "Router",
19
+ # .asyncio.server
20
+ "basic_auth",
21
+ "broadcast",
22
+ "serve",
23
+ "unix_serve",
24
+ "ServerConnection",
25
+ "Server",
26
+ # .client
27
+ "ClientProtocol",
28
+ # .datastructures
29
+ "Headers",
30
+ "HeadersLike",
31
+ "MultipleValuesError",
32
+ # .exceptions
33
+ "ConcurrencyError",
34
+ "ConnectionClosed",
35
+ "ConnectionClosedError",
36
+ "ConnectionClosedOK",
37
+ "DuplicateParameter",
38
+ "InvalidHandshake",
39
+ "InvalidHeader",
40
+ "InvalidHeaderFormat",
41
+ "InvalidHeaderValue",
42
+ "InvalidMessage",
43
+ "InvalidOrigin",
44
+ "InvalidParameterName",
45
+ "InvalidParameterValue",
46
+ "InvalidProxy",
47
+ "InvalidProxyMessage",
48
+ "InvalidProxyStatus",
49
+ "InvalidState",
50
+ "InvalidStatus",
51
+ "InvalidUpgrade",
52
+ "InvalidURI",
53
+ "NegotiationError",
54
+ "PayloadTooBig",
55
+ "ProtocolError",
56
+ "ProxyError",
57
+ "SecurityError",
58
+ "WebSocketException",
59
+ # .frames
60
+ "Close",
61
+ "CloseCode",
62
+ "Frame",
63
+ "Opcode",
64
+ # .http11
65
+ "Request",
66
+ "Response",
67
+ # .protocol
68
+ "Protocol",
69
+ "Side",
70
+ "State",
71
+ # .server
72
+ "ServerProtocol",
73
+ # .typing
74
+ "Data",
75
+ "ExtensionName",
76
+ "ExtensionParameter",
77
+ "LoggerLike",
78
+ "StatusLike",
79
+ "Origin",
80
+ "Subprotocol",
81
+ ]
82
+
83
+ # When type checking, import non-deprecated aliases eagerly. Else, import on demand.
84
+ if TYPE_CHECKING:
85
+ from .asyncio.client import ClientConnection, connect, unix_connect
86
+ from .asyncio.router import Router, route, unix_route
87
+ from .asyncio.server import (
88
+ Server,
89
+ ServerConnection,
90
+ basic_auth,
91
+ broadcast,
92
+ serve,
93
+ unix_serve,
94
+ )
95
+ from .client import ClientProtocol
96
+ from .datastructures import Headers, HeadersLike, MultipleValuesError
97
+ from .exceptions import (
98
+ ConcurrencyError,
99
+ ConnectionClosed,
100
+ ConnectionClosedError,
101
+ ConnectionClosedOK,
102
+ DuplicateParameter,
103
+ InvalidHandshake,
104
+ InvalidHeader,
105
+ InvalidHeaderFormat,
106
+ InvalidHeaderValue,
107
+ InvalidMessage,
108
+ InvalidOrigin,
109
+ InvalidParameterName,
110
+ InvalidParameterValue,
111
+ InvalidProxy,
112
+ InvalidProxyMessage,
113
+ InvalidProxyStatus,
114
+ InvalidState,
115
+ InvalidStatus,
116
+ InvalidUpgrade,
117
+ InvalidURI,
118
+ NegotiationError,
119
+ PayloadTooBig,
120
+ ProtocolError,
121
+ ProxyError,
122
+ SecurityError,
123
+ WebSocketException,
124
+ )
125
+ from .frames import Close, CloseCode, Frame, Opcode
126
+ from .http11 import Request, Response
127
+ from .protocol import Protocol, Side, State
128
+ from .server import ServerProtocol
129
+ from .typing import (
130
+ Data,
131
+ ExtensionName,
132
+ ExtensionParameter,
133
+ LoggerLike,
134
+ Origin,
135
+ StatusLike,
136
+ Subprotocol,
137
+ )
138
+ else:
139
+ lazy_import(
140
+ globals(),
141
+ aliases={
142
+ # .asyncio.client
143
+ "connect": ".asyncio.client",
144
+ "unix_connect": ".asyncio.client",
145
+ "ClientConnection": ".asyncio.client",
146
+ # .asyncio.router
147
+ "route": ".asyncio.router",
148
+ "unix_route": ".asyncio.router",
149
+ "Router": ".asyncio.router",
150
+ # .asyncio.server
151
+ "basic_auth": ".asyncio.server",
152
+ "broadcast": ".asyncio.server",
153
+ "serve": ".asyncio.server",
154
+ "unix_serve": ".asyncio.server",
155
+ "ServerConnection": ".asyncio.server",
156
+ "Server": ".asyncio.server",
157
+ # .client
158
+ "ClientProtocol": ".client",
159
+ # .datastructures
160
+ "Headers": ".datastructures",
161
+ "HeadersLike": ".datastructures",
162
+ "MultipleValuesError": ".datastructures",
163
+ # .exceptions
164
+ "ConcurrencyError": ".exceptions",
165
+ "ConnectionClosed": ".exceptions",
166
+ "ConnectionClosedError": ".exceptions",
167
+ "ConnectionClosedOK": ".exceptions",
168
+ "DuplicateParameter": ".exceptions",
169
+ "InvalidHandshake": ".exceptions",
170
+ "InvalidHeader": ".exceptions",
171
+ "InvalidHeaderFormat": ".exceptions",
172
+ "InvalidHeaderValue": ".exceptions",
173
+ "InvalidMessage": ".exceptions",
174
+ "InvalidOrigin": ".exceptions",
175
+ "InvalidParameterName": ".exceptions",
176
+ "InvalidParameterValue": ".exceptions",
177
+ "InvalidProxy": ".exceptions",
178
+ "InvalidProxyMessage": ".exceptions",
179
+ "InvalidProxyStatus": ".exceptions",
180
+ "InvalidState": ".exceptions",
181
+ "InvalidStatus": ".exceptions",
182
+ "InvalidUpgrade": ".exceptions",
183
+ "InvalidURI": ".exceptions",
184
+ "NegotiationError": ".exceptions",
185
+ "PayloadTooBig": ".exceptions",
186
+ "ProtocolError": ".exceptions",
187
+ "ProxyError": ".exceptions",
188
+ "SecurityError": ".exceptions",
189
+ "WebSocketException": ".exceptions",
190
+ # .frames
191
+ "Close": ".frames",
192
+ "CloseCode": ".frames",
193
+ "Frame": ".frames",
194
+ "Opcode": ".frames",
195
+ # .http11
196
+ "Request": ".http11",
197
+ "Response": ".http11",
198
+ # .protocol
199
+ "Protocol": ".protocol",
200
+ "Side": ".protocol",
201
+ "State": ".protocol",
202
+ # .server
203
+ "ServerProtocol": ".server",
204
+ # .typing
205
+ "Data": ".typing",
206
+ "ExtensionName": ".typing",
207
+ "ExtensionParameter": ".typing",
208
+ "LoggerLike": ".typing",
209
+ "Origin": ".typing",
210
+ "StatusLike": ".typing",
211
+ "Subprotocol": ".typing",
212
+ },
213
+ deprecated_aliases={
214
+ # deprecated in 9.0 - 2021-09-01
215
+ "framing": ".legacy",
216
+ "handshake": ".legacy",
217
+ "parse_uri": ".uri",
218
+ "WebSocketURI": ".uri",
219
+ # deprecated in 14.0 - 2024-11-09
220
+ # .legacy.auth
221
+ "BasicAuthWebSocketServerProtocol": ".legacy.auth",
222
+ "basic_auth_protocol_factory": ".legacy.auth",
223
+ # .legacy.client
224
+ "WebSocketClientProtocol": ".legacy.client",
225
+ # .legacy.exceptions
226
+ "AbortHandshake": ".legacy.exceptions",
227
+ "InvalidStatusCode": ".legacy.exceptions",
228
+ "RedirectHandshake": ".legacy.exceptions",
229
+ "WebSocketProtocolError": ".legacy.exceptions",
230
+ # .legacy.protocol
231
+ "WebSocketCommonProtocol": ".legacy.protocol",
232
+ # .legacy.server
233
+ "WebSocketServer": ".legacy.server",
234
+ "WebSocketServerProtocol": ".legacy.server",
235
+ },
236
+ )
@@ -0,0 +1,5 @@
1
+ from .cli import main
2
+
3
+
4
+ if __name__ == "__main__":
5
+ main()