File size: 2,500 Bytes
7916f15
d023036
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7916f15
d023036
 
 
 
 
 
 
ff15b41
d023036
 
ff15b41
d023036
 
ff15b41
d023036
 
 
 
 
ff15b41
 
d023036
 
 
 
 
 
 
 
ff15b41
d023036
9204fa7
d023036
 
 
41d3274
d023036
 
 
 
 
 
7916f15
 
 
d023036
 
 
 
 
 
 
 
 
7916f15
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import asyncio
import json
import os
import secrets
import signal
from collections import defaultdict
from websockets import serve, ConnectionClosed

# Dictionary to store editor content for each user
editor_content = defaultdict(str)

# Set of connected users
connected_users = set()


async def broadcast_changes(websocket, user, change):
    """
    Broadcast code editing changes to all connected clients.
    """
    event = {
        "type": "edit",
        "user": user,
        "change": change,
    }

    # Broadcast the change to all connected clients
    for user_socket in connected_users:
        if user_socket != websocket:
            await user_socket.send(json.dumps(event))


async def handle_code_editing(websocket, user):
    """
    Handle code editing events from a user.
    """
    connected_users.add(websocket)

    try:
        # Send the initial state of the document to the new user
        await websocket.send(json.dumps({"type": "init", "content": editor_content[user]}))

        async for message in websocket:
            event = json.loads(message)
            assert event["type"] == "edit"
            change = event["change"]

            # Apply the change to the document
            editor_content[user] = change["text"]

            # Broadcast the change to all connected clients
            await broadcast_changes(websocket, user, change)

    except ConnectionClosed:
        pass
    finally:
        # Remove the user when the WebSocket connection is closed
        connected_users.remove(websocket)


async def editor_handler(websocket, path):
    """
    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)
    assert event["type"] == "init"

    user = event.get("user", None)

    if not user:
        # Assign a random username for simplicity
        user = secrets.token_urlsafe(8)

    try:
        await handle_code_editing(websocket, user)
    except Exception as e:
        print(f"Error: {e}")
    finally:
        connected_users.remove(websocket)


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)

    
    async with serve(editor_handler, "0.0.0.0", 7860):
        await stop


if __name__ == "__main__":
    asyncio.run(main())