#!/usr/bin/env python import asyncio import json import os import secrets import signal import subprocess import websockets from connect4 import PLAYER1, PLAYER2, Connect4 from user import User JOIN = {} WATCH = {} async def create_file(websocket, project_name, path, connected): file_path = os.path.join(os.getcwd(), 'projects', project_name, path) # Create the file with open(file_path, 'w'): pass websockets.broadcast(connected,f"file created to {file_path}") async def create_folder(websocket, project_name, path, connected): folder_path = os.path.join(os.getcwd(), 'projects', project_name, path) # Create the folder os.makedirs(folder_path) websockets.broadcast(connected,f"folder created to {folder_path}") async def wirte_file(websocket, project_name, path, content, connected): try: file_path = os.path.join(os.getcwd(), 'projects', project_name, path) file_content = content with open(file_path, 'w', encoding='utf-8') as file: file.write(file_content) websockets.broadcast(connected,f"Content written to {file_path}") except FileNotFoundError as e: websockets.broadcast(connected,f"Error: {str(e)}") async def execute_command(websocket, project_name, command, connected): base_path = os.path.join(os.getcwd(), 'projects', project_name) try: process = await asyncio.create_subprocess_shell( command, cwd=base_path, stdin=asyncio.subprocess.PIPE, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, # text=True, ) async def send_message(message): print('sending msg') # await websocket.send(f'data: {message}') websockets.broadcast(connected,f'data: {message}' ) async for line in process.stdout: print('sending line') print(line.strip()) # await process_input_request(line) await send_message(line.strip()) print(line.strip()) async for line in process.stderr: print(f'error:{line.strip()}') await send_message(f'error:{line.strip()}') return_code = await process.wait() if return_code == 0: await send_message('Code executed successfully') # pass else: await send_message(f'error:Execution failed with return code {return_code}') # Send the command output to the client # output, error = process.communicate() # response = f"Command executed:\n\nOutput:\n{output.decode('utf-8')}\nError:\n{error.decode('utf-8')}" # await websocket.send(response) except Exception as e: await websocket.send(e) async def create_file_structure(websocket, data, base_path='.'): if data['type'] == 'folder': folder_path = os.path.join(base_path, data['name']) os.makedirs(folder_path, exist_ok=True) for child in data['children']: await create_file_structure(websocket,child, base_path=folder_path) elif data['type'] == 'file': file_path = os.path.join(base_path, data['name']) with open(file_path, 'w', encoding='utf-8') as file: file.write(data['content']) event = { "type": "msg", "message": "project created", } await websocket.send(json.dumps(event)) async def error(websocket, message): """ Send an error message. """ event = { "type": "error", "message": message, } await websocket.send(json.dumps(event)) async def replay(websocket, game): """ Send previous moves. """ # Make a copy to avoid an exception if game.moves changes while iteration # is in progress. If a move is played while replay is running, moves will # be sent out of order but each move will be sent once and eventually the # UI will be consistent. for player, column, row in game.moves.copy(): event = { "type": "play", "player": player, "column": column, "row": row, } await websocket.send(json.dumps(event)) async def play(websocket, game, player, connected): """ Receive and process moves from a player. """ async for message in websocket: # Parse a "play" event from the UI. event = json.loads(message) assert event["type"] == "play" column = event["column"] try: # Play the move. row = game.play(player, column) except RuntimeError as exc: # Send an "error" event if the move was illegal. await error(websocket, str(exc)) continue # Send a "play" event to update the UI. event = { "type": "play", "player": player, "column": column, "row": row, } websockets.broadcast(connected, json.dumps(event)) # If move is winning, send a "win" event. if game.winner is not None: event = { "type": "win", "player": game.winner, } websockets.broadcast(connected, json.dumps(event)) async def exe(websocket,connected): """ Receive and process moves from a player. """ print('in exe') async for message in websocket: # Parse a "play" event from the UI. event = json.loads(message) assert event["type"] == "cmd" # command = event["command"] try: if "command" in event: await execute_command(websocket, event["project_name"], event["command"], connected) elif "write" in event: await wirte_file(websocket, event["project_name"], event["path"], event["content"], connected) elif "create" in event: await wirte_file(websocket, event["project_name"], event["path"], event["content"], connected) #Todo elif "delete" in event: await wirte_file(websocket, event["project_name"], event["path"], event["content"], connected) #Todo elif "rename" in event: await wirte_file(websocket, event["project_name"], event["path"], event["content"], connected) #Todo else: # First player starts a new game. await start(websocket, message) except RuntimeError as exc: # Send an "error" event if the move was illegal. await error(websocket, str(exc)) continue # # Send a "play" event to update the UI. # event = { # "type": "play", # "player": player, # "column": column, # "row": row, # } # websockets.broadcast(connected, json.dumps(event)) # # If move is winning, send a "win" event. # if game.winner is not None: # event = { # "type": "win", # "player": game.winner, # } # websockets.broadcast(connected, json.dumps(event)) async def start(websocket,events): """ Handle a connection from the first player: start a new game. """ # Initialize a Connect Four game, the set of WebSocket connections # receiving moves from this game, and secret access tokens. game = User() connected = {websocket} join_key = secrets.token_urlsafe(12) JOIN[join_key] = game, connected watch_key = secrets.token_urlsafe(12) WATCH[watch_key] = game, connected try: # Send the secret access tokens to the browser of the first player, # where they'll be used for building "join" and "watch" links. event = { "type": "init", "join": join_key, "watch": watch_key, } await websocket.send(json.dumps(event)) js = json.loads(events) assert js["type"] == "init" base_path = os.path.join(os.getcwd(), 'projects', js["project_name"]) data=json.loads(js["file_structure"]) # Receive and process moves from the first player. # await play(websocket, game, PLAYER1, connected) await create_file_structure(websocket,data, base_path=base_path) await exe(websocket,connected) finally: del JOIN[join_key] del WATCH[watch_key] async def join(websocket, join_key): """ Handle a connection from the second player: join an existing game. """ # Find the Connect Four game. try: game, connected = JOIN[join_key] except KeyError: await error(websocket, "Game not found.") return # Register to receive moves from this game. connected.add(websocket) try: # Send the first move, in case the first player already played it. await replay(websocket, game) # Receive and process moves from the second player. await play(websocket, game, PLAYER2, connected) finally: connected.remove(websocket) async def watch(websocket, watch_key): """ Handle a connection from a spectator: watch an existing game. """ # Find the Connect Four game. try: game, connected = WATCH[watch_key] except KeyError: await error(websocket, "Game not found.") return # Register to receive moves from this game. connected.add(websocket) try: # Send previous moves, in case the game already started. await replay(websocket, game) # Keep the connection open, but don't receive any messages. await websocket.wait_closed() finally: connected.remove(websocket) async def handler(websocket): """ Handle a connection and dispatch it according to who is connecting. """ # Receive and parse the "init" event from the UI. message = await websocket.recv() event = json.loads(message) print(event) project_name = event["project_name"] # assert event["type"] == "init" if event["type"] == "init": if "join" in event: # Second player joins an existing game. await join(websocket, event["join"]) elif "watch" in event: # Spectator watches an existing game. await watch(websocket, event["watch"]) else: # First player starts a new game. await start(websocket, message) elif event["type"] == "cmd": print('executing commad') # Execute a command in the project folder. await execute_command(websocket, event["project_name"], event["command"]) # assert event["type"] == "cmd" # if "command" in event: # # Second player joins an existing game. # await execute_command(websocket, project_name, event["command"]) # elif "watch" in event: # # Spectator watches an existing game. # await watch(websocket, event["watch"]) # else: # # First player starts a new game. # await start(websocket, message) async def main(): # Set the stop condition when receiving SIGTERM. loop = asyncio.get_running_loop() stop = loop.create_future() loop.add_signal_handler(signal.SIGTERM, stop.set_result, None) port = int(os.environ.get("PORT", "7860")) async with websockets.serve(handler, "0.0.0.0", port): await stop if __name__ == "__main__": asyncio.run(main())