async def server(websocket, path): try: user_data = { "send": NativeFunction( None, [], None, lambda message: Cmd(lambda _: lambda: websocket.send( message)), ), "disconnect": NativeFunction( None, [], None, lambda: Cmd(lambda: lambda: websocket.close() ), # TODO: add code and reason ), "ip": tuple([int(i) for i in websocket.remote_address[0].split(".")]), "uuid": str(uuid.uuid4()), } stop = await options["onConnect"].run([user_data, path]) if isinstance(stop, Cmd): stop = await stop.eval() if stop: ws_server.close() async for message in websocket: stop = await options["onMessage"].run([user_data, message]) if isinstance(stop, Cmd): stop = await stop.eval() if stop: ws_server.close() if websocket.closed: stop = await options["onDisconnect"].run([user_data, none]) if isinstance(stop, Cmd): stop = await stop.eval() if stop: ws_server.close() except websockets.exceptions.ConnectionClosed as err: stop = await options["onDisconnect"].run( [user_data, yes({ "code": err.code, "reason": err.reason })]) if isinstance(stop, Cmd): stop = await stop.eval() if stop: ws_server.close()
async def on_message(): try: async for message in websocket: if debug: print( f"[{url}] {Fore.CYAN}recv{Style.RESET_ALL} {Fore.MAGENTA}{message}{Style.RESET_ALL}" ) close = await options["onMessage"].run([send, message]) if debug: print( f"[{url}] {Fore.BLUE}onMessage handled.{Style.RESET_ALL}" ) if isinstance(close, Cmd): close = await close.eval() if close: await websocket.close() if not on_close.done(): on_close.set_result( None) # Mark `on_close` as done except websockets.exceptions.ConnectionClosedError as err: if debug: print( f"[{url}] {Fore.CYAN}ERROR{Style.RESET_ALL} {Fore.RED}{err}{Style.RESET_ALL}" ) return yes(err.reason)
async def read(path): try: async with async_open(path, "r", encoding="utf-8") as f: return yes(await f.read()) except: return none return ()
async def on_connect(): try: close = await options["onOpen"].run([send]) if debug: print( f"[{url}] {Fore.BLUE}onOpen handled.{Style.RESET_ALL}" ) if isinstance(close, Cmd): close = await close.eval() if close: await websocket.close() if not on_close.done(): on_close.set_result( None) # Mark `on_close` as done # await options['onClose'].eval() if debug: debug_task.cancel() except websockets.exceptions.ConnectionClosedError as err: if debug: print( f"[{url}] {Fore.CYAN}ERROR{Style.RESET_ALL} {Fore.RED}{err}{Style.RESET_ALL}" ) return yes(err.reason)
async def readBytes(path): try: async with async_open(path, "rb") as f: return yes(list(await f.read())) except: return none
def item_at(lis, index): if index < 0 or index >= len(lis): return none else: return yes(lis[index])
def char_at(string, index): if index < 0 or index >= len(string): return none else: return yes(string[index])
def parse_int(string): try: return yes(int(string)) except: return none
def parse_float(string): try: return yes(float(string)) if math.isfinite(float(string)) else none except: return none
def to_module(possible_module): if isinstance(possible_module, NModule): return yes(NModuleWrapper(possible_module)) return none
def map_get(key, map): item = map.get(key) if item is None: return none else: return yes(item)
def parseSafe(string): try: return yes(python_to_json(json.loads(string))) except: return none
async def connect(options, url): debug = os.environ.get("N_WS_DEBUG") == "experimental" if debug: print( f"{Fore.YELLOW}Websocket debugging is experimental and may be removed in the future.{Style.RESET_ALL}" ) print(f"[{url}] {Fore.BLUE}Connecting.{Style.RESET_ALL}") async def manual_send(): try: while True: message = await async_input( f"[{url}] NOTICE ME! Type a message and press enter to send.\n" ) print( f"[{url}] {Fore.CYAN}send*{Style.RESET_ALL} {Fore.GREEN}{message}{Style.RESET_ALL}" ) await websocket.send(message) except asyncio.CancelledError: pass debug_task = asyncio.create_task(manual_send()) try: async with websockets.connect(url) as websocket: if debug: print(f"[{url}] {Fore.BLUE}Open!{Style.RESET_ALL}") async def send_msg(message): if debug: print( f"[{url}] {Fore.CYAN}send{Style.RESET_ALL} {Fore.GREEN}{message}{Style.RESET_ALL}" ) try: await websocket.send(message) return ok(()) except websockets.exceptions.ConnectionClosed as err: # Ignore all runtime errors (eg when attempting sending to a closed websocket) return error(err.code) # Why is this so complicated send = NativeFunction( None, [], None, lambda message: Cmd(lambda _: lambda: send_msg(message))) async def on_message(): try: async for message in websocket: if debug: print( f"[{url}] {Fore.CYAN}recv{Style.RESET_ALL} {Fore.MAGENTA}{message}{Style.RESET_ALL}" ) close = await options["onMessage"].run([send, message]) if debug: print( f"[{url}] {Fore.BLUE}onMessage handled.{Style.RESET_ALL}" ) if isinstance(close, Cmd): close = await close.eval() if close: await websocket.close() if not on_close.done(): on_close.set_result( None) # Mark `on_close` as done except websockets.exceptions.ConnectionClosedError as err: if debug: print( f"[{url}] {Fore.CYAN}ERROR{Style.RESET_ALL} {Fore.RED}{err}{Style.RESET_ALL}" ) return yes(err.reason) async def on_connect(): try: close = await options["onOpen"].run([send]) if debug: print( f"[{url}] {Fore.BLUE}onOpen handled.{Style.RESET_ALL}" ) if isinstance(close, Cmd): close = await close.eval() if close: await websocket.close() if not on_close.done(): on_close.set_result( None) # Mark `on_close` as done # await options['onClose'].eval() if debug: debug_task.cancel() except websockets.exceptions.ConnectionClosedError as err: if debug: print( f"[{url}] {Fore.CYAN}ERROR{Style.RESET_ALL} {Fore.RED}{err}{Style.RESET_ALL}" ) return yes(err.reason) on_close = asyncio.Future() asyncio.create_task(on_message()) asyncio.create_task(on_connect()) await on_close if debug: print(f"[{url}] {Fore.BLUE}Closed.{Style.RESET_ALL}") return none except Exception as err: return yes(str(err))