Compare commits
4 Commits
dc549fc23d
...
bc678d8b7b
| Author | SHA1 | Date | |
|---|---|---|---|
| bc678d8b7b | |||
| 60c995cd6d | |||
| 5faccf1c72 | |||
| c25455623f |
@@ -1,8 +1,9 @@
|
|||||||
credentials = {
|
credentials = {
|
||||||
"session_auth": "", # Supply session auth
|
"session_auth": "",
|
||||||
}
|
}
|
||||||
|
|
||||||
log_level = "DEBUG"
|
log_level = "INFO"
|
||||||
|
log_directory = "./logs"
|
||||||
|
|
||||||
vms = {
|
vms = {
|
||||||
"vm1": "wss://computernewb.com/collab-vm/vm1",
|
"vm1": "wss://computernewb.com/collab-vm/vm1",
|
||||||
@@ -12,4 +13,6 @@ vms = {
|
|||||||
"vm5": "wss://computernewb.com/collab-vm/vm5",
|
"vm5": "wss://computernewb.com/collab-vm/vm5",
|
||||||
"vm6": "wss://computernewb.com/collab-vm/vm6",
|
"vm6": "wss://computernewb.com/collab-vm/vm6",
|
||||||
"vm7": "wss://computernewb.com/collab-vm/vm7",
|
"vm7": "wss://computernewb.com/collab-vm/vm7",
|
||||||
|
"vm8": "wss://computernewb.com/collab-vm/vm8",
|
||||||
|
"vm9": "wss://computernewb.com/collab-vm/vm9",
|
||||||
}
|
}
|
||||||
211
cvmsentry.py
211
cvmsentry.py
@@ -1,69 +1,16 @@
|
|||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
from enum import IntEnum
|
||||||
from cvmlib import guac_decode, guac_encode
|
from cvmlib import guac_decode, guac_encode
|
||||||
import config
|
import config
|
||||||
import os
|
import os
|
||||||
import websocket
|
import websockets, asyncio
|
||||||
|
from websockets import Subprotocol, Origin
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
import rel
|
from datetime import datetime, timezone
|
||||||
import threading
|
import json
|
||||||
import multiprocessing
|
|
||||||
import argparse
|
|
||||||
|
|
||||||
LOG_LEVEL = getattr(config, 'log_level', 'INFO')
|
LOG_LEVEL = getattr(config, "log_level", "INFO")
|
||||||
user_state = 0 # 0 = disconnected, 1 = connected, 2 = logged in
|
|
||||||
|
|
||||||
def full_login(ws: websocket.WebSocketApp):
|
|
||||||
# Connect
|
|
||||||
vm_name = os.path.basename(ws.url)
|
|
||||||
ws.send(guac_encode("connect", vm_name))
|
|
||||||
ws.send(guac_encode("login", config.credentials["session_auth"]))
|
|
||||||
|
|
||||||
def on_message(ws, message: str):
|
|
||||||
global user_state
|
|
||||||
if guac_decode(message) is not None:
|
|
||||||
decoded: Optional[List[str]] = guac_decode(message)
|
|
||||||
match decoded:
|
|
||||||
case ["nop"]:
|
|
||||||
ws.send(guac_encode("nop"))
|
|
||||||
case ['connect', '1', '1', '1', '0']:
|
|
||||||
print("Connected")
|
|
||||||
user_state = 1
|
|
||||||
case ["auth", "https://auth.collabvm.org"]:
|
|
||||||
full_login(ws)
|
|
||||||
case ["login", "1"]:
|
|
||||||
print("Logged in")
|
|
||||||
user_state = 2
|
|
||||||
case _: # For the XTREME!!!
|
|
||||||
if decoded is not None:
|
|
||||||
if decoded[0] == "sync" or decoded[0] == "png":
|
|
||||||
return
|
|
||||||
elif decoded[0] == "adduser":
|
|
||||||
number_of_users = int(decoded[1])
|
|
||||||
|
|
||||||
if user_state == 0 and number_of_users == 1:
|
|
||||||
return # Ignore ourselves...
|
|
||||||
|
|
||||||
# print(f"Number of users: {number_of_users}")
|
|
||||||
# users = decoded[2:]
|
|
||||||
# print(f"Users: {users}")
|
|
||||||
elif decoded[0] == "chat":
|
|
||||||
user = "System" if len(decoded[1]) == 0 else decoded[1]
|
|
||||||
message = decoded[2]
|
|
||||||
log.info(f"[{user}]: {message}")
|
|
||||||
|
|
||||||
# print(f"Received: {decoded}")
|
|
||||||
|
|
||||||
def on_error(ws, error):
|
|
||||||
|
|
||||||
print(error)
|
|
||||||
|
|
||||||
def on_close(ws, close_status_code, close_msg):
|
|
||||||
print("### closed ###")
|
|
||||||
|
|
||||||
def on_open(ws):
|
|
||||||
ws.send(guac_encode("rename",""))
|
|
||||||
# websocket.enableTrace(True)
|
|
||||||
|
|
||||||
# Prepare logs
|
# Prepare logs
|
||||||
if not os.path.exists("logs"):
|
if not os.path.exists("logs"):
|
||||||
@@ -79,23 +26,133 @@ log.addHandler(stdout_handler)
|
|||||||
|
|
||||||
log.info(f"CVM-Sentry started")
|
log.info(f"CVM-Sentry started")
|
||||||
|
|
||||||
# Parse the command-line argument
|
|
||||||
parser = argparse.ArgumentParser(description="CVM-Sentry")
|
|
||||||
parser.add_argument("vm_name", type=str, help="Name of the VM to connect to (e.g., vm7)")
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
# Ensure the provided VM name exists in the config
|
class CollabVMState(IntEnum):
|
||||||
if args.vm_name not in config.vms:
|
WS_CONNECTED = 0
|
||||||
log.error(f"VM '{args.vm_name}' not found in configuration.")
|
VM_CONNECTED = 1
|
||||||
sys.exit(1)
|
LOGGED_IN = 2
|
||||||
|
|
||||||
# Use the provided VM name to connect
|
class CollabVMRank(IntEnum):
|
||||||
ws = websocket.WebSocketApp(config.vms[args.vm_name],
|
UNREGISTERED = 0
|
||||||
on_open=on_open,
|
REGISTERED = 1
|
||||||
on_message=on_message,
|
ADMIN = 2
|
||||||
on_error=on_error,
|
MOD = 3
|
||||||
on_close=on_close,
|
|
||||||
header={"User-Agent": "cvmsentry"}, subprotocols=["guacamole"])
|
users = {}
|
||||||
ws.run_forever(dispatcher=rel, reconnect=5) # Set dispatcher to automatic reconnection, 5 second reconnect delay if connection closed unexpectedly
|
vm_botuser = {}
|
||||||
rel.signal(2, rel.abort) # Keyboard Interrupt
|
|
||||||
rel.dispatch()
|
def get_origin_from_ws_url(ws_url: str) -> str:
|
||||||
|
domain = (
|
||||||
|
ws_url.removeprefix("ws:")
|
||||||
|
.removeprefix("wss:")
|
||||||
|
.removeprefix("/")
|
||||||
|
.removeprefix("/")
|
||||||
|
.split("/", 1)[0]
|
||||||
|
)
|
||||||
|
is_wss = ws_url.startswith("wss:")
|
||||||
|
return f"http{'s' if is_wss else ''}://{domain}/"
|
||||||
|
|
||||||
|
|
||||||
|
async def connect(vm_name: str):
|
||||||
|
if vm_name not in config.vms:
|
||||||
|
log.error(f"VM '{vm_name}' not found in configuration.")
|
||||||
|
return
|
||||||
|
uri = config.vms[vm_name]
|
||||||
|
STATE = None
|
||||||
|
log_file_path = os.path.join(getattr(config, "log_directory", "logs"), f"{vm_name}.json")
|
||||||
|
if not os.path.exists(log_file_path):
|
||||||
|
with open(log_file_path, "w") as log_file:
|
||||||
|
log_file.write("{}")
|
||||||
|
async with websockets.connect(
|
||||||
|
uri=uri,
|
||||||
|
subprotocols=[Subprotocol("guacamole")],
|
||||||
|
origin=Origin(get_origin_from_ws_url(uri)),
|
||||||
|
) as websocket:
|
||||||
|
log.info(f"Connected to VM '{vm_name}' at {uri}")
|
||||||
|
STATE = CollabVMState.WS_CONNECTED
|
||||||
|
await websocket.send(guac_encode("rename", ""))
|
||||||
|
await websocket.send(guac_encode("connect", vm_name))
|
||||||
|
if vm_name not in users:
|
||||||
|
users[vm_name] = {}
|
||||||
|
# response = await websocket.recv()
|
||||||
|
async for message in websocket:
|
||||||
|
decoded: Optional[List[str]] = guac_decode(str(message))
|
||||||
|
match decoded:
|
||||||
|
case ["nop"]:
|
||||||
|
await websocket.send(guac_encode("nop"))
|
||||||
|
log.debug((f"({CollabVMState(STATE).name}) Received: {decoded}"))
|
||||||
|
case ["auth", "https://auth.collabvm.org"]:
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
await websocket.send(
|
||||||
|
guac_encode("login", config.credentials["session_auth"])
|
||||||
|
)
|
||||||
|
case ["connect", "1", "1", "1", "0"]:
|
||||||
|
STATE = CollabVMState.VM_CONNECTED
|
||||||
|
log.debug((f"({CollabVMState(STATE).name} - {vm_name}) Connected"))
|
||||||
|
case ["login", "1"]:
|
||||||
|
STATE = CollabVMState.LOGGED_IN
|
||||||
|
case _:
|
||||||
|
if decoded is not None:
|
||||||
|
if decoded[0] in ("sync", "png", "flag", "turn", "size"):
|
||||||
|
continue
|
||||||
|
elif decoded[0] == "chat":
|
||||||
|
user = "System" if len(decoded[1]) == 0 else decoded[1]
|
||||||
|
if user == "System" or STATE < CollabVMState.LOGGED_IN:
|
||||||
|
continue
|
||||||
|
message = decoded[2]
|
||||||
|
log.info(f"[{vm_name} - {user}]: {message}")
|
||||||
|
utc_now = datetime.now(timezone.utc)
|
||||||
|
utc_day = utc_now.strftime("%Y-%m-%d")
|
||||||
|
timestamp = utc_now.isoformat()
|
||||||
|
|
||||||
|
with open(log_file_path, "r+") as log_file:
|
||||||
|
try:
|
||||||
|
log_data = json.load(log_file)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
log_data = {}
|
||||||
|
|
||||||
|
if utc_day not in log_data:
|
||||||
|
log_data[utc_day] = []
|
||||||
|
|
||||||
|
log_data[utc_day].append({
|
||||||
|
"timestamp": timestamp,
|
||||||
|
"username": user,
|
||||||
|
"message": message
|
||||||
|
})
|
||||||
|
|
||||||
|
log_file.seek(0)
|
||||||
|
json.dump(log_data, log_file, indent=4)
|
||||||
|
log_file.truncate()
|
||||||
|
continue
|
||||||
|
elif decoded[0] == "adduser":
|
||||||
|
if STATE == CollabVMState.LOGGED_IN:
|
||||||
|
username = decoded[2]
|
||||||
|
rank = CollabVMRank(int(decoded[3]))
|
||||||
|
users[vm_name][username] = rank
|
||||||
|
elif STATE < CollabVMState.LOGGED_IN:
|
||||||
|
initial_user_payload = decoded[2:]
|
||||||
|
for i in range(0, len(initial_user_payload), 2):
|
||||||
|
username = initial_user_payload[i]
|
||||||
|
rank = CollabVMRank(int(initial_user_payload[i + 1]))
|
||||||
|
users[vm_name][username] = rank
|
||||||
|
elif decoded[0] == "remuser":
|
||||||
|
if STATE == CollabVMState.LOGGED_IN:
|
||||||
|
username = decoded[2]
|
||||||
|
if username in users[vm_name]:
|
||||||
|
del users[vm_name][username]
|
||||||
|
log.debug(
|
||||||
|
(
|
||||||
|
f"({CollabVMState(STATE).name} - {vm_name}) Received: {decoded}"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
for vm in config.vms.keys():
|
||||||
|
|
||||||
|
|
||||||
|
def start_vm_thread(vm_name: str):
|
||||||
|
asyncio.run(connect(vm_name))
|
||||||
|
async def main():
|
||||||
|
tasks = [connect(vm) for vm in config.vms.keys()]
|
||||||
|
await asyncio.gather(*tasks)
|
||||||
|
|
||||||
|
asyncio.run(main())
|
||||||
86
poetry.lock
generated
86
poetry.lock
generated
@@ -12,23 +12,85 @@ files = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "websocket-client"
|
name = "websockets"
|
||||||
version = "1.8.0"
|
version = "15.0.1"
|
||||||
description = "WebSocket client for Python with low level API options"
|
description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.9"
|
||||||
groups = ["main"]
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526"},
|
{file = "websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b"},
|
||||||
{file = "websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da"},
|
{file = "websockets-15.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205"},
|
||||||
|
{file = "websockets-15.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5756779642579d902eed757b21b0164cd6fe338506a8083eb58af5c372e39d9a"},
|
||||||
|
{file = "websockets-15.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fdfe3e2a29e4db3659dbd5bbf04560cea53dd9610273917799f1cde46aa725e"},
|
||||||
|
{file = "websockets-15.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c2529b320eb9e35af0fa3016c187dffb84a3ecc572bcee7c3ce302bfeba52bf"},
|
||||||
|
{file = "websockets-15.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac1e5c9054fe23226fb11e05a6e630837f074174c4c2f0fe442996112a6de4fb"},
|
||||||
|
{file = "websockets-15.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5df592cd503496351d6dc14f7cdad49f268d8e618f80dce0cd5a36b93c3fc08d"},
|
||||||
|
{file = "websockets-15.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0a34631031a8f05657e8e90903e656959234f3a04552259458aac0b0f9ae6fd9"},
|
||||||
|
{file = "websockets-15.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d00075aa65772e7ce9e990cab3ff1de702aa09be3940d1dc88d5abf1ab8a09c"},
|
||||||
|
{file = "websockets-15.0.1-cp310-cp310-win32.whl", hash = "sha256:1234d4ef35db82f5446dca8e35a7da7964d02c127b095e172e54397fb6a6c256"},
|
||||||
|
{file = "websockets-15.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:39c1fec2c11dc8d89bba6b2bf1556af381611a173ac2b511cf7231622058af41"},
|
||||||
|
{file = "websockets-15.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431"},
|
||||||
|
{file = "websockets-15.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57"},
|
||||||
|
{file = "websockets-15.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905"},
|
||||||
|
{file = "websockets-15.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562"},
|
||||||
|
{file = "websockets-15.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792"},
|
||||||
|
{file = "websockets-15.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413"},
|
||||||
|
{file = "websockets-15.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8"},
|
||||||
|
{file = "websockets-15.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3"},
|
||||||
|
{file = "websockets-15.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf"},
|
||||||
|
{file = "websockets-15.0.1-cp311-cp311-win32.whl", hash = "sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85"},
|
||||||
|
{file = "websockets-15.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065"},
|
||||||
|
{file = "websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3"},
|
||||||
|
{file = "websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665"},
|
||||||
|
{file = "websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2"},
|
||||||
|
{file = "websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215"},
|
||||||
|
{file = "websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5"},
|
||||||
|
{file = "websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65"},
|
||||||
|
{file = "websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe"},
|
||||||
|
{file = "websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4"},
|
||||||
|
{file = "websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597"},
|
||||||
|
{file = "websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9"},
|
||||||
|
{file = "websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7"},
|
||||||
|
{file = "websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931"},
|
||||||
|
{file = "websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675"},
|
||||||
|
{file = "websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151"},
|
||||||
|
{file = "websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22"},
|
||||||
|
{file = "websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f"},
|
||||||
|
{file = "websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8"},
|
||||||
|
{file = "websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375"},
|
||||||
|
{file = "websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d"},
|
||||||
|
{file = "websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4"},
|
||||||
|
{file = "websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa"},
|
||||||
|
{file = "websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561"},
|
||||||
|
{file = "websockets-15.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5f4c04ead5aed67c8a1a20491d54cdfba5884507a48dd798ecaf13c74c4489f5"},
|
||||||
|
{file = "websockets-15.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abdc0c6c8c648b4805c5eacd131910d2a7f6455dfd3becab248ef108e89ab16a"},
|
||||||
|
{file = "websockets-15.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a625e06551975f4b7ea7102bc43895b90742746797e2e14b70ed61c43a90f09b"},
|
||||||
|
{file = "websockets-15.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d591f8de75824cbb7acad4e05d2d710484f15f29d4a915092675ad3456f11770"},
|
||||||
|
{file = "websockets-15.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47819cea040f31d670cc8d324bb6435c6f133b8c7a19ec3d61634e62f8d8f9eb"},
|
||||||
|
{file = "websockets-15.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac017dd64572e5c3bd01939121e4d16cf30e5d7e110a119399cf3133b63ad054"},
|
||||||
|
{file = "websockets-15.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4a9fac8e469d04ce6c25bb2610dc535235bd4aa14996b4e6dbebf5e007eba5ee"},
|
||||||
|
{file = "websockets-15.0.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363c6f671b761efcb30608d24925a382497c12c506b51661883c3e22337265ed"},
|
||||||
|
{file = "websockets-15.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2034693ad3097d5355bfdacfffcbd3ef5694f9718ab7f29c29689a9eae841880"},
|
||||||
|
{file = "websockets-15.0.1-cp39-cp39-win32.whl", hash = "sha256:3b1ac0d3e594bf121308112697cf4b32be538fb1444468fb0a6ae4feebc83411"},
|
||||||
|
{file = "websockets-15.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7643a03db5c95c799b89b31c036d5f27eeb4d259c798e878d6937d71832b1e4"},
|
||||||
|
{file = "websockets-15.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0c9e74d766f2818bb95f84c25be4dea09841ac0f734d1966f415e4edfc4ef1c3"},
|
||||||
|
{file = "websockets-15.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1009ee0c7739c08a0cd59de430d6de452a55e42d6b522de7aa15e6f67db0b8e1"},
|
||||||
|
{file = "websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76d1f20b1c7a2fa82367e04982e708723ba0e7b8d43aa643d3dcd404d74f1475"},
|
||||||
|
{file = "websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f29d80eb9a9263b8d109135351caf568cc3f80b9928bccde535c235de55c22d9"},
|
||||||
|
{file = "websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b359ed09954d7c18bbc1680f380c7301f92c60bf924171629c5db97febb12f04"},
|
||||||
|
{file = "websockets-15.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:cad21560da69f4ce7658ca2cb83138fb4cf695a2ba3e475e0559e05991aa8122"},
|
||||||
|
{file = "websockets-15.0.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7f493881579c90fc262d9cdbaa05a6b54b3811c2f300766748db79f098db9940"},
|
||||||
|
{file = "websockets-15.0.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:47b099e1f4fbc95b701b6e85768e1fcdaf1630f3cbe4765fa216596f12310e2e"},
|
||||||
|
{file = "websockets-15.0.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67f2b6de947f8c757db2db9c71527933ad0019737ec374a8a6be9a956786aaf9"},
|
||||||
|
{file = "websockets-15.0.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d08eb4c2b7d6c41da6ca0600c077e93f5adcfd979cd777d747e9ee624556da4b"},
|
||||||
|
{file = "websockets-15.0.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b826973a4a2ae47ba357e4e82fa44a463b8f168e1ca775ac64521442b19e87f"},
|
||||||
|
{file = "websockets-15.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:21c1fa28a6a7e3cbdc171c694398b6df4744613ce9b36b1a498e816787e28123"},
|
||||||
|
{file = "websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f"},
|
||||||
|
{file = "websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
docs = ["Sphinx (>=6.0)", "myst-parser (>=2.0.0)", "sphinx-rtd-theme (>=1.1.0)"]
|
|
||||||
optional = ["python-socks", "wsaccel"]
|
|
||||||
test = ["websockets"]
|
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.1"
|
lock-version = "2.1"
|
||||||
python-versions = ">=3.13"
|
python-versions = ">=3.13"
|
||||||
content-hash = "6c1a29fff5885b0314a617154c590e6695b6e3dcde83268ec426427b3d2e9757"
|
content-hash = "8d7ef4822e036e31c42b4c1124625dde29a878de850e0b8bba3270f8f5e3a367"
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ readme = "README.md"
|
|||||||
requires-python = ">=3.13"
|
requires-python = ">=3.13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rel (>=0.4.9.21,<0.5.0.0)",
|
"rel (>=0.4.9.21,<0.5.0.0)",
|
||||||
"websocket-client (>=1.8.0,<2.0.0)",
|
"websockets (>=15.0.1,<16.0.0)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user