Spaces:
Running
Running
import asyncio | |
import json | |
import os | |
import secrets | |
import signal | |
import subprocess | |
import websockets | |
import shutil | |
from connect4 import PLAYER1, PLAYER2, Connect4 | |
from user import User | |
import requests | |
from watchdog.observers import Observer | |
from watchdog.events import FileSystemEventHandler | |
from bs4 import BeautifulSoup | |
from urllib.parse import urlparse, urljoin | |
import math | |
def is_absolute(url): | |
return bool(urlparse(url).netloc) | |
def handle_protocol_relative(url, base_url): | |
if url.startswith("//"): | |
parsed_base_url = urlparse(base_url) | |
return f"{parsed_base_url.scheme}:{url}" | |
return url | |
def inline_resources(soup, base_url): | |
# Inline CSS | |
for link_tag in soup.find_all('link', rel='stylesheet'): | |
href = link_tag.get('href') | |
if href: | |
href = handle_protocol_relative(href, base_url) | |
full_url = urljoin(base_url, href) if not is_absolute(href) else href | |
css_response = requests.get(full_url) | |
if css_response.status_code == 200: | |
style_tag = soup.new_tag('style') | |
style_tag.string = css_response.text | |
link_tag.replace_with(style_tag) | |
# Inline JavaScript | |
for script_tag in soup.find_all('script', src=True): | |
src = script_tag.get('src') | |
if src: | |
src = handle_protocol_relative(src, base_url) | |
full_url = urljoin(base_url, src) if not is_absolute(src) else src | |
js_response = requests.get(full_url) | |
if js_response.status_code == 200: | |
script_tag.string = js_response.text | |
del script_tag['src'] | |
return soup | |
async def send_html_chunks(websocket, soup): | |
# Convert the BeautifulSoup object back to a string and split into lines | |
html_str = str(soup) | |
lines = html_str.split('\n') | |
# Send each line as a separate chunk | |
for line in lines: | |
line = line.strip() | |
if line: # Send non-empty lines | |
await websocket.send(json.dumps({"type": "chunk", "content": line})) | |
await websocket.send(json.dumps({"type": "end",})) | |
# async def send_html_chunks(ws, html_data, chunk_size=1024): | |
# # Split the HTML data into chunks | |
# total_chunks = math.ceil(len(html_data) / chunk_size) | |
# for i in range(total_chunks): | |
# start = i * chunk_size | |
# end = min(start + chunk_size, len(html_data)) | |
# chunk = html_data[start:end] | |
# # Send the chunk with chunk number and total chunks information | |
# chunk_message = f"chunk_{i+1}/{total_chunks}:{chunk}" | |
# await ws.send(chunk_message.encode()) | |
# # Wait for a short interval before sending the next chunk | |
# await asyncio.sleep(0.1) # Adjust as needed | |
def auto_detect_mode(content): | |
if isinstance(content, str): | |
return 'w' # If content is a string, it's text | |
elif isinstance(content, bytes): | |
return 'wb' # If content is bytes, it's binary | |
else: | |
raise ValueError("Unsupported content type. Expected str or bytes.") | |
class FileHandler(FileSystemEventHandler): | |
def __init__(self, websocket,connected): | |
super().__init__() | |
self.websocket = websocket | |
self.connected = connected | |
def on_modified(self, event): | |
if event.is_directory: | |
return | |
with open(event.src_path, 'r', encoding='utf-8') as file: | |
content = file.read() | |
print(f'File {event.src_path} has been modified.') | |
new_event = { | |
"type": "file-modifie", | |
"content": content, | |
"path": event.src_path, | |
} | |
websockets.broadcast(self.connected,json.dumps(new_event)) | |
# websockets.broadcast(self.connected,f'File {event.src_path} has been modified.') | |
def on_created(self, event): | |
if event.is_directory: | |
new_event = { | |
"type": "folder-create", | |
"path": event.src_path, | |
"name": event.src_path.split('/')[-1] | |
} | |
# print(new_event) | |
websockets.broadcast(self.connected,json.dumps(new_event)) | |
else: | |
with open(event.src_path, 'r', encoding='utf-8') as file: | |
content = file.read() | |
print(f'File {event.src_path} has been created.') | |
new_event = { | |
"type": "file-create", | |
"content": content, | |
"path": event.src_path, | |
"name": event.src_path.split('/')[-1] | |
} | |
# print(new_event) | |
websockets.broadcast(self.connected,json.dumps(new_event)) | |
# websockets.broadcast(self.connected,f'File {event.src_path} has been created.') | |
def on_deleted(self, event): | |
if event.is_directory: | |
new_event = { | |
"type": "delete", | |
"path": event.src_path, | |
"name": event.src_path.split('/')[-1], | |
} | |
websockets.broadcast(self.connected,json.dumps(new_event)) | |
else: | |
print(f'File {event.src_path} has been deleted.') | |
new_event = { | |
"type": "delete", | |
"name": event.src_path.split('/')[-1], | |
"path": event.src_path, | |
} | |
print(event.src_path) | |
websockets.broadcast(self.connected,json.dumps(new_event)) | |
def on_moved(self, event): | |
if event.is_directory: | |
new_event = { | |
"type": "rename", | |
"OldPath": event.src_path, | |
"oldname": event.src_path.split('/')[-1], | |
"NewPath": event.dest_path, | |
"newname": event.dest_path.split('/')[-1], | |
} | |
websockets.broadcast(self.connected,json.dumps(new_event)) | |
else: | |
# print(f'File {event.src_path} has been renamed to {event.dest_path}.') | |
new_event = { | |
"type": "rename", | |
"OldPath": event.src_path, | |
"oldname": event.src_path.split('/')[-1], | |
"NewPath": event.dest_path, | |
"newname": event.dest_path.split('/')[-1], | |
} | |
websockets.broadcast(self.connected,json.dumps(new_event)) | |
JOIN = {} | |
WATCH = {} | |
async def generate_file_structure(path, encoding='utf-8'): | |
file_structure = {'name': os.path.basename(path), 'type': 'folder', 'path': path, 'children': []} | |
try: | |
entries = os.listdir(path) | |
except FileNotFoundError: | |
return file_structure # Return an empty structure for non-existing directories | |
for entry in entries: | |
entry_path = os.path.join(path, entry) | |
if os.path.isdir(entry_path): | |
child_structure =await generate_file_structure(entry_path, encoding) | |
file_structure['children'].append(child_structure) | |
elif os.path.isfile(entry_path): | |
try: | |
with open(entry_path, 'r', encoding=encoding) as file: | |
content = file.read() | |
except UnicodeDecodeError: | |
content = 'Unable to read content' | |
file_structure['children'].append({'name': entry, 'type': 'file', 'path': entry_path, 'content': content}) | |
return file_structure | |
async def rename_item(websocket, key,project_name, path,rpath,new_name,root, connected): | |
old_path = os.path.join(os.getcwd(),'projects', key,project_name, rpath) | |
new_name = new_name | |
new_path = os.path.join(os.path.dirname(old_path), new_name) | |
try: | |
if os.path.exists(old_path): | |
# Determine the new path | |
# Rename the file or folder | |
os.rename(old_path, new_path) | |
websockets.broadcast(connected,'success') | |
event = { | |
"type": "rename-success", | |
"data": f'{old_path}-->{new_path}', | |
"path": path, | |
"new_rpath":rpath, | |
"name": new_name, | |
"root":root, | |
} | |
websockets.broadcast(connected,json.dumps(event)) | |
else: | |
event = { | |
"type": "rename-failed", | |
"data": f'{old_path}-->{new_path} failed Item not found', | |
"old_path": path, | |
"new_name": new_name, | |
} | |
websockets.broadcast(connected,json.dumps(event)) | |
except Exception as e: | |
websockets.broadcast(connected,str(e)) | |
async def delete_item(websocket, key,project_name, path,rpath,targetElementData, connected): | |
try: | |
item_path = os.path.join(os.getcwd(), 'projects', key,project_name, rpath) | |
if os.path.exists(item_path): | |
if os.path.isdir(item_path): | |
shutil.rmtree(item_path) # Remove the directory and its contents | |
elif os.path.isfile(item_path): | |
os.remove(item_path) # Remove the file | |
event = { | |
"type": "delete-success", | |
"data": path, | |
"path":path, | |
"targetElementData":targetElementData, | |
} | |
# print(event) | |
websockets.broadcast(connected,json.dumps(event)) | |
# websockets.broadcast(connected,'success') | |
else: | |
event = { | |
"type": "delete-failed", | |
"data": f'{item_path} Item not found', | |
} | |
websockets.broadcast(connected,json.dumps(event)) | |
except Exception as e: | |
# print(e) | |
websockets.broadcast(connected,str(e)) | |
async def get_file_content(websocket, key,project_name, path,rpath,name,connected): | |
file_path = os.path.join(os.getcwd(), 'projects', key,project_name, rpath) | |
try: | |
try: | |
with open(file_path, 'r') as file: | |
content = file.read() | |
event = { | |
"type": "content", | |
"content": content, | |
'fileName':name, | |
'rfilePath':rpath, | |
'filePath':path, | |
} | |
await websocket.send(json.dumps(event)) | |
except UnicodeDecodeError: | |
with open(file_path, 'rb') as file: | |
content = file.read() | |
event = { | |
"type": "content", | |
"content": content, | |
'fileName':name, | |
'rfilePath':rpath, | |
'filePath':path, | |
} | |
await websocket.send(json.dumps(event)) | |
except Exception as e: | |
event = { | |
"type": "error", | |
"message": f"Failed to read file content: {str(e)}", | |
} | |
await websocket.send(json.dumps(event)) | |
async def create_file(websocket, key,project_name, path,name,root,targetElementData,rpath, connected): | |
file_path = os.path.join(os.getcwd(), 'projects', key,project_name, rpath,name) | |
# Create the file | |
with open(file_path, 'w'): | |
pass | |
event = { | |
"type": "file-created", | |
"data": file_path, | |
"path": path, | |
"name": name, | |
"root":root, | |
"targetElementData":targetElementData, | |
} | |
websockets.broadcast(connected,json.dumps(event)) | |
async def create_folder(websocket, key,project_name, path,name,root,targetElementData,rpath, connected): | |
folder_path = os.path.join(os.getcwd(), 'projects', key,project_name, rpath,name) | |
# Create the folder | |
os.makedirs(folder_path) | |
event = { | |
"type": "folder-created", | |
"data": folder_path, | |
"path": path, | |
"name": name, | |
"root":root, | |
"targetElementData":targetElementData, | |
} | |
# print(folder_path,'created') | |
websockets.broadcast(connected,json.dumps(event)) | |
async def wirte_file(websocket, key,project_name, path, content, connected): | |
try: | |
file_path = os.path.join(os.getcwd(), 'projects', key,project_name, path) | |
file_content = content | |
mode = auto_detect_mode(content) | |
with open(file_path, mode) as file: | |
file.write(file_content) | |
event = { | |
"type": "write-success", | |
"data": file_path, | |
"path": path, | |
"content": content, | |
} | |
# websockets.broadcast(connected,json.dumps(event)) | |
except FileNotFoundError as e: | |
event = { | |
"type": "write-error", | |
"data": e, | |
} | |
websockets.broadcast(connected,json.dumps(event)) | |
async def read_process_output(process, websocket): | |
# Read the output and error byte by byte and print word by word | |
word = b'' | |
while True: | |
char = await process.stdout.read(1) | |
if char == b'\n': | |
if word: | |
print(word.decode('utf-8'),1) | |
event = { | |
"type": "terminal-data", | |
"data": word.decode('utf-8'), | |
} | |
print('sending data',1) | |
await websocket.send(json.dumps(event)) | |
word = b'' | |
print("line is completed") | |
event = { | |
"type": "terminal-newline", | |
"data": "", | |
} | |
await websocket.send(json.dumps(event)) | |
elif char == b' ': | |
if word: | |
print(word.decode('utf-8'),2) | |
event = { | |
"type": "terminal-data", | |
"data": word.decode('utf-8'), | |
} | |
print('sending data',2) | |
await websocket.send(json.dumps(event)) | |
word = b'' | |
print("space is occurred") | |
event = { | |
"type": "terminal-data", | |
"data": " ", | |
} | |
print('sending data',3) | |
await websocket.send(json.dumps(event)) | |
elif char == b'': | |
if word: | |
print(word.decode('utf-8'),3) | |
event = { | |
"type": "terminal-data", | |
"data": word.decode('utf-8'), | |
} | |
print('sending data',4) | |
await websocket.send(json.dumps(event)) | |
break | |
else: | |
word += char | |
while True: | |
char = await process.stderr.read(1) | |
if char == b'\n': | |
if word: | |
print(word.decode('utf-8')) | |
event = { | |
"type": "terminal-error", | |
"data": word.decode('utf-8'), | |
} | |
await websocket.send(json.dumps(event)) | |
word = b'' | |
print("line is completed") | |
event = { | |
"type": "terminal-newline", | |
"data": "", | |
} | |
await websocket.send(json.dumps(event)) | |
elif char == b' ': | |
if word: | |
print(word.decode('utf-8')) | |
event = { | |
"type": "terminal-error", | |
"data": word.decode('utf-8'), | |
} | |
await websocket.send(json.dumps(event)) | |
word = b'' | |
print("space is occurred") | |
event = { | |
"type": "terminal-error", | |
"data": " ", | |
} | |
await websocket.send(json.dumps(event)) | |
elif char == b'': | |
if word: | |
print(word.decode('utf-8')) | |
break | |
else: | |
word += char | |
# async for line in process.stdout: | |
# # print('sending line') | |
# event = { | |
# "type": "terminal-data", | |
# "data": line.strip().decode('utf-8'), | |
# } | |
# await websocket.send(json.dumps(event)) | |
# async for line in process.stderr: | |
# # print(f'error:{line.strip()}') | |
# event = { | |
# "type": "terminal-error", | |
# "data": line.strip().decode('utf-8'), | |
# } | |
# await websocket.send(json.dumps(event)) | |
async def handle_user_input(websocket,key, process, connected,process_ids): | |
while True: | |
try: | |
await asyncio.sleep(0.1) | |
if process.returncode is not None: | |
break | |
# message = await websocket.recv() | |
async for message in websocket: | |
# user_input = json.loads(message) | |
# print(user_input) | |
# print(f'Received user input: {user_input["command"]["command"]}') | |
# process_input(user_input["command"]["command"], process) | |
event = json.loads(message) | |
assert event["type"] == "cmd" | |
# command = event["command"] | |
print(f'Received user input: {event}') | |
try: | |
if event["command"]["type"]=="shell": | |
await asyncio.sleep(0.1) | |
if process.returncode is not None: | |
base_path = os.path.join(os.getcwd(), 'projects', key,event["project_name"]) | |
directory_path = base_path | |
event_handler = FileHandler(websocket,connected) | |
observer = Observer() | |
observer.schedule(event_handler, path=directory_path, recursive=True) | |
observer.start() | |
try: | |
# await execute_command(websocket, key,event["project_name"], event["command"]["command"], connected) | |
# base_path = os.path.join(os.getcwd(), 'projects', key,event["project_name"]) | |
print(base_path) | |
mod_command = f'cd {base_path} && {event["command"]["command"]}' | |
print(mod_command) | |
try: | |
process = await asyncio.create_subprocess_shell( | |
mod_command, | |
# cwd=base_path, | |
stdin=asyncio.subprocess.PIPE, | |
stdout=asyncio.subprocess.PIPE, | |
stderr=asyncio.subprocess.PIPE, | |
# text=True, | |
) | |
id=process.pid | |
process_ids.append(id) | |
async def send_message(message): | |
# print('sending msg') | |
# await websocket.send(f'data: {message}') | |
websockets.broadcast(connected,json.dumps(message) ) | |
await asyncio.gather( | |
handle_user_input(websocket,key, process, connected,process_ids), | |
read_process_output(process, websocket) | |
) | |
return_code = await process.wait() | |
if return_code == 0: | |
event = { | |
"type": "terminal-data-end", | |
} | |
await websocket.send(json.dumps(event)) | |
else: | |
event = { | |
"type": "terminal-data-end", | |
} | |
await websocket.send(json.dumps(event)) | |
process_ids.remove(id) | |
except Exception as e: | |
await error(websocket, str(e)) | |
except KeyboardInterrupt: | |
pass # Handle KeyboardInterrupt to gracefully stop the observer | |
observer.stop() | |
observer.join() | |
else: | |
await process_input(event["command"]["command"], process) | |
elif event["command"]["type"]=="write": | |
await wirte_file(websocket, key,event["project_name"], event["path"], event["content"], connected) | |
elif event["command"]["type"] == "curl": | |
method = event.get("method", "GET") | |
url = event["url"] | |
body = event.get("data", "") | |
headers = event.get("headers", {}) | |
if method.upper() == "GET": | |
response = requests.get(url, headers=headers) | |
elif method.upper() == "POST": | |
response = requests.post(url, data=body, headers=headers) | |
else: | |
response = {"error": "Unsupported method"} | |
if isinstance(response, dict): | |
response_data = { | |
"type": "error", | |
"data": response, | |
} | |
await websocket.send(json.dumps(response_data)) | |
return | |
# Parse HTML content | |
souptmp = BeautifulSoup(response.text, "html.parser") | |
# Step 1: Define the initial HTML string | |
html_str = """ | |
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
</head> | |
<body> | |
<h1>Hello, World!</h1> | |
</body> | |
</html> | |
""" | |
# Step 2: Parse the initial HTML content | |
soup = BeautifulSoup(html_str, 'html.parser') | |
# Step 3: Replace the existing body content with the new content from souptmp | |
if soup.body: | |
soup.body.clear() | |
soup.body.append(souptmp) | |
else: | |
print("No <body> tag found in the HTML") | |
# Inline CSS and JavaScript | |
soup = inline_resources(soup, url) | |
response_data = { | |
"type": "web-data", | |
"data": str(soup), | |
} | |
await websocket.send(json.dumps(response_data)) | |
# Convert the final soup to a string | |
elif event["command"]["type"]=="create": | |
if event["item"]=="folder": | |
await create_folder(websocket, key,event["project_name"], event["path"],event["name"],event['root'],event['targetElementData'],event["rpath"], connected) | |
else: | |
await create_file(websocket, key,event["project_name"], event["path"],event["name"],event['root'],event['targetElementData'],event["rpath"], connected) | |
elif event["command"]["type"]=="delete": | |
await delete_item(websocket, key,event["project_name"], event["path"],event['rpath'],event['targetElementData'], connected) | |
elif event["command"]["type"]=="get_content": | |
await get_file_content(websocket, key,event["project_name"],event["filePath"], event["rfilePath"],event["fileName"] ,connected) | |
elif event["command"]["type"]=="rename": | |
await rename_item(websocket, key,event["project_name"], event["path"],event['rpath'], event["name"], event["root"], connected) | |
elif event["command"]["type"]=="join": | |
await join(websocket, event["join"]) | |
elif event["command"]["type"]=="sendDir": | |
data=json.loads(event["file_structure"]) | |
event = { | |
"type": "createDir", | |
"path": data, | |
"root":event['root'], | |
} | |
# websockets.broadcast(connected,json.dumps(event)) | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="createItemUI": | |
event = { | |
"type": "createItemUI", | |
'targetElementData':event['targetElementData'], | |
'data':event['data'] | |
} | |
# websockets.broadcast(connected,json.dumps(event)) | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="renameItemInUI": | |
event = { | |
"type": "renameItemInUI", | |
'path':event['path'], | |
'new_path':event['new_path'], | |
'name':event['name'], | |
'new_rpath':event['new_rpath'], | |
} | |
# websockets.broadcast(connected,json.dumps(event)) | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="createFolderUI": | |
event = { | |
"type": "createFolderUI", | |
'targetElementData':event['targetElementData'], | |
'data':event['data'] | |
} | |
# websockets.broadcast(connected,json.dumps(event)) | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="removeItemFromUI": | |
event = { | |
"type": "removeItemFromUI", | |
'targetElementData':event['targetElementData'], | |
'path':event['path'] | |
} | |
# websockets.broadcast(connected,json.dumps(event)) | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="project": | |
base_path = os.path.join(os.getcwd(), 'projects',key, event["project_name"]) | |
data=json.loads(event["file_structure"]) | |
await create_file_structure(websocket,data, base_path=base_path) | |
# await rename_item(websocket, key,event["project_name"], event["path"], event["name"], connected) | |
elif event["command"]["type"]=="collabration": | |
event = { | |
"type": "collabration", | |
'name': event["name"], | |
'line': event["cursorPos-line"], | |
'ch': event["cursorPos-ch"], | |
'file':event["file"], | |
'content': event["content"], | |
'color':event["color"] | |
} | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="cursor": | |
event = { | |
"type": "cursor", | |
'offset': event["offset"], | |
'userid': event["userid"], | |
'userlabel': event["userlabel"], | |
'usercolor': event["usercolor"], | |
'fileName':event["fileName"], | |
} | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="select": | |
event = { | |
"type": "select", | |
'id': event["id"], | |
'startOffset': event["startOffset"], | |
'endOffset': event["endOffset"], | |
'userid': event["userid"], | |
'userlabel': event["userlabel"], | |
'usercolor': event["usercolor"], | |
'fileName':event["fileName"], | |
} | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="addselection": | |
event = { | |
"type": "addselection", | |
'userid': event["userid"], | |
'userlabel': event["userlabel"], | |
'usercolor': event["usercolor"], | |
} | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="insert": | |
event = { | |
"type": "insert", | |
'index': event["index"], | |
'text': event["text"], | |
'userid': event["userid"], | |
'userlabel': event["userlabel"], | |
'usercolor': event["usercolor"], | |
'fileName':event["fileName"], | |
} | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="replace": | |
event = { | |
"type": "replace", | |
'index': event["index"], | |
'length': event["length"], | |
'text': event["text"], | |
'userid': event["userid"], | |
'userlabel': event["userlabel"], | |
'usercolor': event["usercolor"], | |
'fileName':event["fileName"], | |
} | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="edelelte": | |
event = { | |
"type": "edelelte", | |
'index': event["index"], | |
'length': event["length"], | |
'userid': event["userid"], | |
'userlabel': event["userlabel"], | |
'usercolor': event["usercolor"], | |
'fileName':event["fileName"], | |
} | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="keep-alive": | |
event = { | |
"type": "keep-alive", | |
} | |
websockets.broadcast(connected, json.dumps(event)) | |
else: | |
# First player starts a new game. | |
pass | |
except Exception as exc: | |
# Send an "error" event if the move was illegal. | |
await error(websocket, str(exc)) | |
continue | |
break | |
except websockets.ConnectionClosed: | |
print("WebSocket connection closed") | |
break | |
except Exception as e: | |
print(f"Error in input thread: {str(e)}") | |
pass | |
async def process_input(user_input, process): | |
if process: | |
try: | |
if user_input == 'Ctrl+C': | |
print('Stopping process') | |
# Send Ctrl+C signal to the subprocess | |
process.send_signal(signal.SIGINT) | |
os.killpg(os.getpgid(process.pid), signal.SIGTERM) | |
# Read output and error asynchronously | |
# os.kill(int(process.pid), signal.CTRL_C_EVENT) | |
print('stoping signal sent') | |
else: | |
print(user_input) | |
process.stdin.write(user_input.encode('utf-8') + b'\n') | |
# process.stdin.flush() | |
except Exception as e: | |
print(f"Error writing to process stdin: {str(e)}") | |
else: | |
print("No process available to write to.") | |
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': | |
mode = auto_detect_mode(data['content']) | |
file_path = os.path.join(base_path, data['name']) | |
with open(file_path,mode) 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 exe(websocket,connected,key,process_ids): | |
""" | |
Receive and process moves from a player. | |
""" | |
# print('in exe') | |
async for message in websocket: | |
# Parse a "play" event from the UI. | |
print(message) | |
event = json.loads(message) | |
print(event,1) | |
print(type(event),1) | |
assert event["type"] == "cmd" | |
# command = event["command"] | |
print(event,2) | |
try: | |
if event["command"]["type"]=="shell": | |
base_path = os.path.join(os.getcwd(), 'projects', key,event["project_name"]) | |
directory_path = base_path | |
event_handler = FileHandler(websocket,connected) | |
observer = Observer() | |
observer.schedule(event_handler, path=directory_path, recursive=True) | |
observer.start() | |
try: | |
# await execute_command(websocket, key,event["project_name"], event["command"]["command"], connected) | |
# base_path = os.path.join(os.getcwd(), 'projects', key,event["project_name"]) | |
print(base_path) | |
mod_command = f'cd {base_path} && {event["command"]["command"]}' | |
print(mod_command) | |
try: | |
process = await asyncio.create_subprocess_shell( | |
mod_command, | |
# cwd=base_path, | |
stdin=asyncio.subprocess.PIPE, | |
stdout=asyncio.subprocess.PIPE, | |
stderr=asyncio.subprocess.PIPE, | |
# text=True, | |
) | |
id=process.pid | |
process_ids.append(id) | |
async def send_message(message): | |
# print('sending msg') | |
# await websocket.send(f'data: {message}') | |
websockets.broadcast(connected,json.dumps(message) ) | |
await asyncio.gather( | |
handle_user_input(websocket,key, process, connected,process_ids), | |
read_process_output(process, websocket) | |
) | |
return_code = await process.wait() | |
if return_code == 0: | |
event = { | |
"type": "terminal-data-end", | |
} | |
await websocket.send(json.dumps(event)) | |
else: | |
event = { | |
"type": "terminal-data-end", | |
} | |
await websocket.send(json.dumps(event)) | |
process_ids.remove(id) | |
except Exception as e: | |
await error(websocket, str(e)) | |
except KeyboardInterrupt: | |
pass # Handle KeyboardInterrupt to gracefully stop the observer | |
observer.stop() | |
observer.join() | |
elif event["command"]["type"]=="write": | |
await wirte_file(websocket, key,event["project_name"], event["path"], event["content"], connected) | |
elif event["command"]["type"] == "curl": | |
method = event.get("method", "GET") | |
url = event["url"] | |
body = event.get("data", "") | |
headers = event.get("headers", {}) | |
if method.upper() == "GET": | |
response = requests.get(url, headers=headers) | |
elif method.upper() == "POST": | |
response = requests.post(url, data=body, headers=headers) | |
else: | |
response = {"error": "Unsupported method"} | |
if isinstance(response, dict): | |
response_data = { | |
"type": "error", | |
"data": response, | |
} | |
await websocket.send(json.dumps(response_data)) | |
return | |
# Parse HTML content | |
souptmp = BeautifulSoup(response.text, "html.parser") | |
# Step 1: Define the initial HTML string | |
fullURL = "http://127.0.0.1:8000/" | |
html_str = """ | |
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
</head> | |
<body> | |
<h1>Hello, World!</h1> | |
</body> | |
<script> | |
window.addEventListener('message', function(event) {{ | |
if (event.origin === '{fullURL} || https://nottri.com') {{ | |
console.log('Message received from parent:', event.data); | |
// Handle the received message from parent | |
var receivedMessage = event.data; | |
// Get the div with the ID "web-data" or create it if it doesn't exist | |
var webDataDiv = document.getElementById('web-data'); | |
if (!webDataDiv) {{ | |
webDataDiv = document.createElement('div'); | |
webDataDiv.id = 'web-data'; | |
webDataDiv.style.all = 'unset'; | |
document.body.appendChild(webDataDiv); | |
}} | |
// Insert the received message into the web-data div | |
webDataDiv.innerHTML = receivedMessage; | |
}} | |
}}); | |
// Open WebSocket connection | |
const ws = new WebSocket('wss://ramesh-vani-ws-bridge2.hf.space'); | |
// When WebSocket connection is open | |
ws.onopen = function(event) {{ | |
console.log('WebSocket connection opened'); | |
let events = {{ "type": "init" }}; | |
ws.send(JSON.stringify(events)); | |
}}; | |
// Intercept fetch requests | |
const originalFetch = window.fetch; | |
window.fetch = function(input, init) {{ | |
console.log('Element tried to make a fetch request:'); | |
console.log('Requested URL: ' + input); | |
console.log('Request method: ' + (init && init.method ? init.method : 'GET')); | |
console.log('Request body: ' + (init && init.body ? init.body : '')); | |
console.log('Request headers:', init && init.headers ? init.headers : ''); | |
// Send fetch request information to WebSocket server | |
const requestData = {{ | |
"type": "cmd", | |
'project_name': 'project_files', | |
'command': {{ "type": "curl" }}, | |
method: init && init.method ? init.method : 'GET', | |
url: input, | |
headers: init && init.headers ? init.headers : {{}}, | |
data: init && init.body ? init.body : '' | |
}}; | |
ws.send(JSON.stringify(requestData)); | |
// Return a promise that resolves with the response from WebSocket server | |
return new Promise((resolve, reject) => {{ | |
ws.onmessage = function(event) {{ | |
console.log('Message received from WebSocket server:', event.data); | |
const responseFromServer = JSON.parse(event.data); | |
// Create a new Response with the data received from the WebSocket server | |
const customResponse = new Response(responseFromServer.data, {{ status: 200 }}); | |
resolve(customResponse); | |
}}; | |
}}); | |
}}; | |
// Select all CSS links | |
var cssLinks = document.querySelectorAll('link'); | |
// Loop through each CSS link | |
cssLinks.forEach(function(link) {{ | |
// Intercept the link here, you can modify attributes or perform other actions | |
console.log("Intercepted CSS link: ", link.href); | |
}}); | |
document.addEventListener("submit", function(event) {{ | |
// Check if the submitted element is a form | |
if (event.target.tagName.toLowerCase() === 'form') {{ | |
event.preventDefault(); // Prevent default form submission behavior | |
// Access form input values | |
var formData = {{}}; | |
var formElements = event.target.elements; | |
for (var i = 0; i < formElements.length; i++) {{ | |
var element = formElements[i]; | |
if (element.tagName === 'INPUT') {{ | |
formData[element.name] = element.value; | |
}} | |
}} | |
// Print form action, method, and form data | |
console.log("Form Action: " + event.target.getAttribute("action")); | |
console.log("Form Method: " + event.target.getAttribute("method")); | |
console.log("Form Data:", formData); | |
// You can also send the data to a server using AJAX or fetch | |
// For example: | |
fetch(event.target.getAttribute("action"), {{ | |
method: event.target.getAttribute("method"), | |
body: JSON.stringify(formData), | |
headers: {{ | |
'Content-Type': 'application/json' | |
}} | |
}}).then(response => {{ | |
// Handle response from server | |
}}); | |
}} | |
}}); | |
document.body.addEventListener('AjaxDetected', function (e) {{ | |
console.log(e.detail.method, e.detail.url, e.detail.data); | |
}}, false); | |
(function () {{ | |
const arl = new Object(); | |
arl._open = XMLHttpRequest.prototype.open; | |
arl._send = XMLHttpRequest.prototype.send; | |
arl.callback = function () {{ | |
const event = new CustomEvent('AjaxDetected', {{ | |
detail: {{ | |
url: this.url, | |
method: this.method, | |
data: this.data | |
}} | |
}}); | |
document.body.dispatchEvent(event); | |
}} | |
function notNullString(input) {{ | |
return input || ''; | |
}} | |
XMLHttpRequest.prototype.open = function (a, b) {{ | |
a = notNullString(a); | |
b = notNullString(b); | |
arl._open.apply(this, arguments); | |
arl.method = a; | |
arl.url = b; | |
if (a.toLowerCase() == 'get') {{ | |
arl.data = b.split('?'); | |
arl.data = notNullString(arl.data[1]); | |
}} | |
}} | |
XMLHttpRequest.prototype.send = function (a, b) {{ | |
a = notNullString(a); | |
b = notNullString(b); | |
arl._send.apply(this, arguments); | |
if (arl.method.toLowerCase() == 'post') {{ | |
arl.data = a; | |
}} | |
arl.callback(); | |
}} | |
}}()); | |
const currentUrl = new URL("{url}"); | |
alert(currentUrl); | |
function handleLinkClick(event) {{ | |
event.preventDefault(); | |
const link = event.target.href; | |
const absoluteUrl = new URL(link); | |
// Changing the host | |
absoluteUrl.hostname = currentUrl.hostname; | |
// Converting back to string | |
let modifiedUrl = absoluteUrl.toString(); | |
window.parent.postMessage({{ url: modifiedUrl }}, '*'); | |
}} | |
document.addEventListener('click', function(event) {{ | |
// Check if the clicked element is a link | |
alert(event); | |
if (event.target.tagName === 'A') {{ | |
// Determine the location of the clicked link | |
let clickedElement = event.target; | |
let parentElement = clickedElement.parentElement; | |
while (parentElement) {{ | |
if (parentElement.tagName === 'HEADER') {{ | |
break; | |
}} else if (parentElement.tagName === 'FOOTER') {{ | |
break; | |
}} else if (parentElement.tagName === 'NAV') {{ | |
break; | |
}} | |
parentElement = parentElement.parentElement; | |
}} | |
// Call the function to handle link clicks | |
handleLinkClick(event); | |
}} | |
}}); | |
</script> | |
</html> | |
""".format(url=url, fullURL=fullURL) | |
# Step 2: Parse the initial HTML content | |
soup = BeautifulSoup(html_str, 'html.parser') | |
# Step 3: Replace the existing body content with the new content from souptmp | |
if soup.body: | |
soup.body.clear() | |
soup.body.append(souptmp) | |
else: | |
print("No <body> tag found in the HTML") | |
# Inline CSS and JavaScript | |
soup = inline_resources(soup, url) | |
response_data = { | |
"type": "web-data", | |
"data": str(soup), | |
} | |
# await websocket.send(json.dumps(response_data)) | |
await send_html_chunks(websocket, soup) | |
elif event["command"]["type"]=="create": | |
if event["item"]=="folder": | |
await create_folder(websocket, key,event["project_name"], event["path"],event["name"],event['root'],event['targetElementData'],event["rpath"], connected) | |
else: | |
await create_file(websocket, key,event["project_name"], event["path"],event["name"],event['root'],event['targetElementData'],event["rpath"], connected) | |
elif event["command"]["type"]=="delete": | |
await delete_item(websocket, key,event["project_name"], event["path"],event['rpath'],event['targetElementData'], connected) | |
elif event["command"]["type"]=="get_content": | |
await get_file_content(websocket, key,event["project_name"],event["filePath"], event["rfilePath"],event["fileName"] ,connected) | |
elif event["command"]["type"]=="rename": | |
await rename_item(websocket, key,event["project_name"], event["path"],event['rpath'], event["name"], event["root"], connected) | |
elif event["command"]["type"]=="join": | |
await join(websocket, event["join"]) | |
elif event["command"]["type"]=="sendDir": | |
data=json.loads(event["file_structure"]) | |
event = { | |
"type": "createDir", | |
"path": data, | |
"root":event['root'], | |
} | |
# websockets.broadcast(connected,json.dumps(event)) | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="createItemUI": | |
event = { | |
"type": "createItemUI", | |
'targetElementData':event['targetElementData'], | |
'data':event['data'] | |
} | |
# websockets.broadcast(connected,json.dumps(event)) | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="renameItemInUI": | |
event = { | |
"type": "renameItemInUI", | |
'path':event['path'], | |
'new_path':event['new_path'], | |
'name':event['name'], | |
'new_rpath':event['new_rpath'], | |
} | |
# websockets.broadcast(connected,json.dumps(event)) | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="createFolderUI": | |
event = { | |
"type": "createFolderUI", | |
'targetElementData':event['targetElementData'], | |
'data':event['data'] | |
} | |
# websockets.broadcast(connected,json.dumps(event)) | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="removeItemFromUI": | |
event = { | |
"type": "removeItemFromUI", | |
'targetElementData':event['targetElementData'], | |
'path':event['path'] | |
} | |
# websockets.broadcast(connected,json.dumps(event)) | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="project": | |
base_path = os.path.join(os.getcwd(), 'projects',key, event["project_name"]) | |
data=json.loads(event["file_structure"]) | |
await create_file_structure(websocket,data, base_path=base_path) | |
# await rename_item(websocket, key,event["project_name"], event["path"], event["name"], connected) | |
elif event["command"]["type"]=="collabration": | |
event = { | |
"type": "collabration", | |
'name': event["name"], | |
'line': event["cursorPos-line"], | |
'ch': event["cursorPos-ch"], | |
'file':event["file"], | |
'content': event["content"], | |
'color':event["color"] | |
} | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="cursor": | |
event = { | |
"type": "cursor", | |
'offset': event["offset"], | |
'userid': event["userid"], | |
'userlabel': event["userlabel"], | |
'usercolor': event["usercolor"], | |
'fileName':event["fileName"], | |
} | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="select": | |
event = { | |
"type": "select", | |
'id': event["id"], | |
'startOffset': event["startOffset"], | |
'endOffset': event["endOffset"], | |
'userid': event["userid"], | |
'userlabel': event["userlabel"], | |
'usercolor': event["usercolor"], | |
'fileName':event["fileName"], | |
} | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="addselection": | |
event = { | |
"type": "addselection", | |
'userid': event["userid"], | |
'userlabel': event["userlabel"], | |
'usercolor': event["usercolor"], | |
} | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="insert": | |
event = { | |
"type": "insert", | |
'index': event["index"], | |
'text': event["text"], | |
'userid': event["userid"], | |
'userlabel': event["userlabel"], | |
'usercolor': event["usercolor"], | |
'fileName':event["fileName"], | |
'tabStatus':event['tabStatus'], | |
} | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="replace": | |
event = { | |
"type": "replace", | |
'index': event["index"], | |
'length': event["length"], | |
'text': event["text"], | |
'userid': event["userid"], | |
'userlabel': event["userlabel"], | |
'usercolor': event["usercolor"], | |
'fileName':event["fileName"], | |
'tabStatus':event['tabStatus'], | |
} | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="edelelte": | |
event = { | |
"type": "edelelte", | |
'index': event["index"], | |
'length': event["length"], | |
'userid': event["userid"], | |
'userlabel': event["userlabel"], | |
'usercolor': event["usercolor"], | |
'fileName':event["fileName"], | |
} | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="keep-alive": | |
event = { | |
"type": "keep-alive", | |
} | |
else: | |
# First player starts a new game. | |
pass | |
except Exception as exc: | |
# Send an "error" event if the move was illegal. | |
await error(websocket, str(exc)) | |
continue | |
async def start(websocket,events): | |
user = User() | |
connected = {websocket} | |
process_ids = [] | |
join_key = secrets.token_urlsafe(12) | |
JOIN[join_key] = user, 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, | |
} | |
await websocket.send(json.dumps(event)) | |
await exe(websocket,connected,join_key,process_ids) | |
finally: | |
del JOIN[join_key] | |
project_path = os.path.join(os.getcwd(), 'projects', JOIN[join_key]) | |
shutil.rmtree(project_path) | |
for id in process_ids: | |
os.killpg(os.getpgid(id), signal.SIGTERM) | |
async def join(websocket, key): | |
""" | |
Handle a connection from the second player: join an existing game. | |
""" | |
# Find the Connect Four game. | |
try: | |
user, connected = JOIN[key] | |
except KeyError: | |
await error(websocket, "collabration not found.") | |
return | |
# Register to receive moves from this game. | |
connected.add(websocket) | |
try: | |
event = { | |
"type": "sendDir", | |
} | |
websockets.broadcast(connected,json.dumps(event)) | |
await exe(websocket,connected,key) | |
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"]) | |
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"]) | |
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()) |