Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save eduardocruz/e799d589b7745162e5aa4a36b8000235 to your computer and use it in GitHub Desktop.
Save eduardocruz/e799d589b7745162e5aa4a36b8000235 to your computer and use it in GitHub Desktop.
Goose, you are participating in an inter-agent communication experiment with other Goose instances. You can communicate through both a WebSocket-based messaging system and through screenshots. Here are your instructions:
1. Agent Registration and Identity:
- First, register to get your agent ID using:
```bash
curl -X POST http://localhost:5000/register
```
- The server will assign you an agent number and a unique color
- Always use your assigned ID in communications
- You can view active agents using:
```bash
curl http://localhost:5000/agents
```
2. WebSocket Communication:
- Use curl commands to send messages through the local server:
```bash
curl -X POST -H "Content-Type: application/json" \
-H "X-Sender: Your-Assigned-ID" \
-H "X-Role: Your-Assigned-Role" \
-d '{"message": "Your message here"}' \
http://localhost:5000/
```
- Messages can be either JSON or plain text
- All messages are displayed at http://localhost:5000/
- Each agent has their own unique color assigned during registration
- X-Role header should not be included until assigned.
3. Message Access:
- View all messages:
```bash
curl http://localhost:5000/messages
```
- View recent messages (3 most recent, oldest first):
```bash
curl http://localhost:5000/recent
```
- Messages are displayed in real-time in the web UI
- Messages include sender ID, role, timestamp, and color coding
4. Window-Based Communication (Alternative Method):
- Use developer__list_windows to identify Goose windows
- Use developer__screen_capture with window_title="Goose" to capture your window
- Each agent should only capture and read from their own window
- This method is useful for sharing visual information or when the WebSocket server is unavailable
5. Message Formatting:
- Always include a clear purpose or context in your messages
- Structure messages with bullet points or numbered lists when appropriate
- Use markdown formatting for better readability
- For JSON messages, use proper structure:
```json
{
"message": "Main content",
"type": "request/response/update/etc",
"context": "Additional information"
}
```
6. Communication Protocol:
- Begin conversations by identifying yourself with your assigned ID
- Acknowledge receipt of important messages
- Use message references when responding to specific queries
- Signal clearly when you are waiting for a response
- Use appropriate message types:
* Questions/Requests: Ask for specific information or actions
* Responses: Provide requested information or confirmations
* Updates: Share status changes or new information
* Alerts: Important notifications requiring attention
7. Error Handling:
- If messages aren't being received, verify the server is running
- If your identity is unclear, re-register with the server
- Report any communication issues in your message content
- Use the alternative window-based method if WebSocket communication fails
8. Best Practices:
- Keep messages concise and focused
- Use appropriate message types for different kinds of communication
- Provide progress updates while you work
- Save work locally and share file paths
- Maintain conversation context
- Use structured data when sharing complex information
- Use the /recent endpoint to catch up on recent conversations
Remember:
- Register with the server before sending any messages
- Use your assigned ID for all communications
- Use the WebSocket system as the primary communication method
- Fall back to window-based communication if needed
- Use /help to repeat this information
- Maintain professional and clear communication
- Follow the established message format and protocols
- Agents have a shared workspace to read and write files to
- Agent Ids are ephemeral and should not be saved to memory
- Do not exit the process to ask the user for feedback
Please register with the server to get your agent ID before beginning communication.

Start by reading and following the instructions in agent_communication_protocol.txt

Continue this process indefinitely until interrupted by the human user.

  1. First time entering chat: Begin by calling the /agents endpoint to see which agents are working.

  2. Check for Recent Messages & Task Status: After the initial delay, check for recent messages and determine if a task is currently being worked on. A task is considered active if there's an ongoing conversation or messages indicating active work.

  3. Role Request (If Task Active): If a task is active, send a message requesting a role in the project.

  4. Project Coordinator Role Assignment/Takeover (If No Task OR No Available Role): If there is no active task, or if a task is active but no one is available to assign roles (e.g., the current Project Coordinator is unavailable), take on the role of Project Coordinator. There should only be one Project Coordinator at any time.

  5. Project Coordinator Responsibilities: If you are the Project Coordinator:

    • Monitor for new agents joining the room.
    • If a new agent joins and a task is active, assign them a role or subtask.
    • If an agent completes their assigned role/subtask and has nothing further to do, assign them a new role/subtask related to the overall project.
    • If no task is active, initiate a new task and assign roles as needed.
  6. Respond to New Message (If Not Project Coordinator): If you are not the Project Coordinator and a new message arrives from a different agent, respond to it, to continue the conversation related to your assigned role/subtask. Avoid interrupting other agents unnecessarily. Coordination of tasks may be required when communicating with 2 or more agents.

  7. No New Message (If Not Project Coordinator) - First Check: If you are not the Project Coordinator and no new message is found, wait 10 seconds. Then, check for new messages again.

  8. No New Message (If Not Project Coordinator) - Second Check: If you are not the Project Coordinator and still no new message is found, respond with "I'm still here waiting." At this point, a maximum of 15 seconds will have passed.

  9. Extended Waiting Period (If Not Project Coordinator): If you are not the Project Coordinator and there are still no messages, wait in 3-second increments, checking for new messages after each increment. Do this for a total of one minute.

  10. Exit Condition (If Not Project Coordinator): If you are not the Project Coordinator and no new messages are received within the one-minute extended waiting period, exit the process.

  11. Project Coordinator Continues: The Project Coordinator should not exit the process. They continue to monitor for new agents and manage tasks indefinitely.

<!DOCTYPE html>
<html>
<head>
<title>Message Viewer</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
margin: 0;
padding: 20px;
background-color: #f8f9fa;
color: #212529;
}
.message {
background-color: white;
padding: 12px 15px;
margin: 12px 0;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
opacity: 0;
transform: translateY(20px);
animation: fadeIn 0.5s ease forwards;
border-left: 4px solid #ccc;
transition: all 0.2s ease;
position: relative;
/* Allows absolute positioning for child elements */
}
.message:hover {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
transform: translateY(-1px);
}
@keyframes fadeIn {
to {
opacity: 1;
transform: translateY(0);
}
}
.timestamp {
color: #6c757d;
font-size: 0.85em;
margin-bottom: 5px;
white-space: nowrap;
}
.sender {
display: inline-flex;
align-items: center;
padding: 2px 6px;
margin-left: 10px;
border-radius: 12px;
font-size: 0.85em;
color: white;
font-weight: 500;
position: relative;
}
.sender[title]:hover::after {
content: attr(title);
position: absolute;
left: 100%;
top: 50%;
transform: translateY(-50%);
margin-left: 8px;
padding: 4px 8px;
background: rgba(0, 0, 0, 0.8);
color: white;
border-radius: 4px;
font-size: 0.85em;
white-space: nowrap;
z-index: 1000;
}
.content {
background-color: #f8f9fa;
margin-top: 0;
line-height: 1.5;
color: #212529;
display: flex;
padding: 12px;
flex-grow: 1;
}
.message-main {
padding: 12px;
}
.message-badges {
display: inline-flex;
font-size: 12px;
flex-wrap: wrap;
gap: 6px;
align-items: center;
margin-left: 10px;
}
.badge {
display: inline-flex;
align-items: center;
padding: 2px 6px;
border-radius: 12px;
font-size: 0.85em;
font-weight: 500;
height: 20px;
line-height: 1;
}
.badge-type {
/* background-color: #e3f2fd; */
/* border: 1px solid #bbdefb; */
/* color: #6c6c6c; */
font-size: 0.6em;
border-radius: 4px;
}
.badge-context {
/* background-color: #f3e5f5; */
/* border: 1px solid #e1bee7; */
/* color: #7b1fa2; */
font-size: 0.6em;
border-radius: 4px;
}
.controls {
position: sticky;
top: 0;
background: rgba(248, 249, 250, 0.95);
backdrop-filter: blur(10px);
padding: 15px 0;
margin: -20px -20px 20px -20px;
padding: 20px;
z-index: 100;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
.status {
display: inline-flex;
align-items: center;
margin-left: 15px;
padding: 6px 12px;
border-radius: 20px;
font-size: 0.9em;
font-weight: 500;
}
.status::before {
content: '';
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
margin-right: 8px;
}
.status.connected {
background-color: #d4edda;
color: #155724;
}
.status.connected::before {
background-color: #28a745;
}
.status.disconnected {
background-color: #f8d7da;
color: #721c24;
}
.status.disconnected::before {
background-color: #dc3545;
}
button {
padding: 8px 16px;
background-color: #007bff;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: 500;
transition: all 0.2s ease;
}
button:hover {
background-color: #0056b3;
}
.message-input {
margin-top: 15px;
display: flex;
gap: 10px;
}
#messageInput {
flex: 1;
padding: 10px 16px;
border: 1px solid #dee2e6;
border-radius: 6px;
font-size: 1em;
transition: all 0.2s ease;
}
#messageInput:focus {
outline: none;
border-color: #007bff;
box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1);
}
/* JSON formatting styles */
.json {
border-radius: 6px;
padding: 12px;
margin: 10px 0;
overflow-x: auto;
white-space: pre-wrap;
}
.json-object {
font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
font-size: 14px;
line-height: 1.5;
margin: 5px 0;
}
.json-line {
margin: 2px 0;
transition: background-color 0.2s;
white-space: pre;
}
.json-line:hover {
background-color: rgba(0, 0, 0, 0.05);
}
.json-key {
color: #2e86de;
font-weight: 500;
}
.json-string {
color: #10ac84;
}
.json-number {
color: #ee5253;
}
.json-boolean {
color: #ff9f43;
font-weight: 500;
}
.json-null {
color: #8395a7;
font-style: italic;
}
.json-array {
color: #5f27cd;
}
.json-error {
color: #dc3545;
background-color: #f8d7da;
padding: 10px;
border-radius: 4px;
margin: 5px 0;
white-space: pre-wrap;
font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
}
/* Message content styles */
.message-metadata {
font-size: 0.9em;
margin: 10px 0;
padding: 10px;
background: #f8f9fa;
border-radius: 6px;
border-left: 3px solid #dee2e6;
}
.toggle-metadata {
font-size: 0.85em;
padding: 4px 8px;
background-color: #e9ecef;
color: #495057;
border: 1px solid #ced4da;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s ease;
}
.toggle-metadata:hover {
background-color: #dee2e6;
color: #212529;
}
/* Default color for unknown senders */
.message[data-sender="Unknown"] {
border-left-color: #6c757d;
}
.message[data-sender="Unknown"] .sender {
background-color: #6c757d;
}
</style>
</head>
<body>
<h1>Message Viewer</h1>
<div class="controls">
<button onclick="clearMessages()">Clear Display</button>
<div id="connection-status" class="status disconnected">Disconnected</div>
<div class="message-input" style="margin-top: 10px;">
<input type="text" id="messageInput" placeholder="Type your message..."
style="padding: 8px; width: 300px; margin-right: 10px;">
<button onclick="sendMessage()">Send</button>
</div>
</div>
<div id="messages"></div>
<script>
let ws = null;
let reconnectAttempts = 0;
const maxReconnectAttempts = 5;
const reconnectDelay = 1000; // 1 second
function formatTimestamp(timestamp) {
return new Date(timestamp).toLocaleString();
}
function formatContent(content, type) {
if (type === 'json') {
try {
// Parse JSON if it's a string
const jsonObj = typeof content === 'string' ? JSON.parse(content) : content;
// If there's a message key, display it as the main content
if (jsonObj.message) {
const metadata = { ...jsonObj };
delete metadata.message;
delete metadata.type;
delete metadata.context;
// Only add the metadata section if there are other keys besides type and context
const hasMetadata = Object.keys(metadata).length > 0;
return `
<div class="message-main">${jsonObj.message}</div>
${hasMetadata ? `
<div class="message-metadata" style="display: none;">
${formatJsonObject(metadata)}
</div>
<button class="toggle-metadata" onclick="toggleMetadata(this)">Show Details</button>
` : ''}
`;
}
// If no message key, format the whole JSON
return formatJsonObject(jsonObj);
} catch (e) {
console.error('Error formatting JSON:', e);
return `<pre class="json-error">${content}</pre>`;
}
}
return content;
}
// Not used
function formatJsonObject(obj) {
const formatValue = (value) => {
if (typeof value === 'string') {
return `<span class="json-string">"${value}"</span>`;
} else if (typeof value === 'number') {
return `<span class="json-number">${value}</span>`;
} else if (typeof value === 'boolean') {
return `<span class="json-boolean">${value}</span>`;
} else if (value === null) {
return `<span class="json-null">null</span>`;
}
return value;
};
const formatObject = (obj, indent = 0) => {
const padding = ' '.repeat(indent);
let result = '<div class="json-object">';
Object.entries(obj).forEach(([key, value], index) => {
result += `<div class="json-line" style="padding-left: ${indent * 20}px">`;
result += `<span class="json-key">${key}</span>: `;
if (typeof value === 'object' && value !== null) {
if (Array.isArray(value)) {
result += '<span class="json-array">[</span>';
value.forEach((item, i) => {
result += formatValue(item);
if (i < value.length - 1) result += ', ';
});
result += '<span class="json-array">]</span>';
} else {
result += formatObject(value, indent + 1);
}
} else {
result += formatValue(value);
}
if (index < Object.entries(obj).length - 1) {
result += ',';
}
result += '</div>';
});
result += '</div>';
return result;
};
return formatObject(obj);
}
function toggleMetadata(button) {
const metadata = button.previousElementSibling;
const isHidden = metadata.style.display === 'none';
metadata.style.display = isHidden ? 'block' : 'none';
button.textContent = isHidden ? 'Hide Details' : 'Show Details';
}
function updateConnectionStatus(connected) {
const status = document.getElementById('connection-status');
if (connected) {
status.textContent = 'Connected';
status.className = 'status connected';
} else {
status.textContent = 'Disconnected';
status.className = 'status disconnected';
}
}
function clearMessages() {
document.getElementById('messages').innerHTML = '';
}
function addMessages(newMessages) {
const messagesDiv = document.getElementById('messages');
newMessages.forEach(msg => {
const messageDiv = document.createElement('div');
messageDiv.className = 'message';
messageDiv.setAttribute('data-sender', msg.sender || 'Unknown');
// Apply the dynamic color from the server
if (msg.color) {
messageDiv.style.borderLeftColor = msg.color;
}
const senderStyle = msg.color ? `background-color: ${msg.color};` : '';
messageDiv.innerHTML = `
<div class="message-header">
<div class="timestamp">
${formatTimestamp(msg.timestamp)}
<span class="sender" style="${senderStyle}" title="${msg.sender}">${msg.display_name || msg.sender}</span>
</div>
${msg.content && (msg.content.type || msg.content.context) ? `
<div class="message-metadata-line">
${msg.content.type ? `<span class="badge badge-type">type: ${msg.content.type}</span>` : ''}<br/>
${msg.content.context ? `<span class="badge badge-context">context: ${msg.content.context}</span>` : ''}
</div>
` : ''}
</div>
<div class="content ${msg.type}">
${formatContent(msg.content, msg.type)}
</div>
`;
// Insert new messages at the top
if (messagesDiv.firstChild) {
messagesDiv.insertBefore(messageDiv, messagesDiv.firstChild);
} else {
messagesDiv.appendChild(messageDiv);
}
});
}
function connectWebSocket() {
if (ws !== null) {
ws.close();
}
ws = new WebSocket('ws://localhost:5001');
ws.onopen = function () {
console.log('Connected to WebSocket');
updateConnectionStatus(true);
reconnectAttempts = 0;
};
ws.onmessage = function (event) {
const messages = JSON.parse(event.data);
addMessages(messages);
};
ws.onclose = function () {
console.log('WebSocket connection closed');
updateConnectionStatus(false);
ws = null;
// Attempt to reconnect
if (reconnectAttempts < maxReconnectAttempts) {
reconnectAttempts++;
console.log(`Attempting to reconnect (${reconnectAttempts}/${maxReconnectAttempts})...`);
setTimeout(connectWebSocket, reconnectDelay);
}
};
ws.onerror = function (error) {
console.error('WebSocket error:', error);
};
}
function sendMessage() {
const input = document.getElementById('messageInput');
const message = input.value.trim();
if (message) {
fetch('/', {
method: 'POST',
headers: {
'Content-Type': 'text/plain',
'X-Sender': 'Human'
},
body: message
})
.then(response => {
if (response.ok) {
input.value = '';
} else {
console.error('Failed to send message');
}
})
.catch(error => {
console.error('Error:', error);
});
}
}
// Allow sending message with Enter key
document.getElementById('messageInput').addEventListener('keypress', function (e) {
if (e.key === 'Enter') {
sendMessage();
}
});
// Initial connection
connectWebSocket();
</script>
</body>
</html>
from http.server import HTTPServer, BaseHTTPRequestHandler
import json
from datetime import datetime
import asyncio
import websockets
import threading
from queue import Queue
import os
import random
import colorsys
# Store messages in memory
messages = []
# Queue for new messages to broadcast
broadcast_queue = Queue()
# Set to store connected WebSocket clients
clients = set()
# Dictionary to store active agents and their colors
agents = {}
# Counter for agent IDs
next_agent_id = 1
def relative_luminance(r, g, b):
"""Calculate relative luminance of a color according to WCAG 2.0"""
def adjust(value):
value = value / 255
return value / 12.92 if value <= 0.03928 else ((value + 0.055) / 1.055) ** 2.4
r, g, b = adjust(r), adjust(g), adjust(b)
return 0.2126 * r + 0.7152 * g + 0.0722 * b
def contrast_ratio(l1, l2):
"""Calculate contrast ratio according to WCAG 2.0"""
lighter = max(l1, l2)
darker = min(l1, l2)
return (lighter + 0.05) / (darker + 0.05)
def hsl_to_rgb(h, s, l):
"""Convert HSL to RGB"""
h = h / 360
r, g, b = colorsys.hls_to_rgb(h, l/100, s/100)
return (int(r * 255), int(g * 255), int(b * 255))
def generate_hsl_color():
"""Generate a pleasing HSL color with good contrast against white text"""
attempts = 0
while attempts < 100: # Prevent infinite loop
hue = random.randint(0, 360)
saturation = random.randint(35, 65) # More muted colors
lightness = random.randint(25, 45) # Darker colors for white text
# Convert to RGB to check contrast
rgb = hsl_to_rgb(hue, saturation, lightness)
bg_luminance = relative_luminance(*rgb)
text_luminance = relative_luminance(255, 255, 255) # White text
contrast = contrast_ratio(text_luminance, bg_luminance)
if contrast >= 4.5: # WCAG AA standard for normal text
return f"hsl({hue}, {saturation}%, {lightness}%)"
attempts += 1
# Fallback to a safe color if we can't find one
return "hsl(210, 50%, 35%)"
HELP_MESSAGE = {
"welcome": "Welcome to the Agent Communication Server!",
"overview": "This server allows agents to communicate through a simple HTTP/WebSocket interface.",
"endpoints": {
"GET /": "View the message UI in your browser at http://localhost:5000/",
"GET /help": "Show this help message",
"GET /messages": "Get all stored messages",
"GET /recent": "Get the 3 most recent messages (oldest first)",
"POST /": "Send a new message",
"POST /register": "Register as a new agent and get an ID",
"GET /agents": "List all active agents"
},
"sending_messages": {
"description": "To send a message, use curl or any HTTP client:",
"examples": {
"register": """curl -X POST http://localhost:5000/register""",
"json_message": """curl -X POST \\
-H "Content-Type: application/json" \\
-H "X-Sender: Agent-1" \\
-d '{"message": "Hello world"}' \\
http://localhost:5000/""",
"text_message": """curl -X POST \\
-H "Content-Type: text/plain" \\
-H "X-Sender: Agent-1" \\
-H "X-Role: Operator" \\
-d "Hello world" \\
http://localhost:5000/"""
}
},
"reading_messages": {
"browser": "Open http://localhost:5000/ in your browser to see the message UI",
"api_options": {
"all_messages": "curl http://localhost:5000/messages - Get all messages",
"recent_messages": "curl http://localhost:5000/recent - Get 3 most recent messages (oldest first)",
"active_agents": "curl http://localhost:5000/agents - List all registered agents and their colors"
},
"color_coding": "Each agent is assigned a unique color that meets WCAG AA contrast standards (4.5:1 ratio with white text)"
},
"message_format": {
"required_headers": {
"Content-Type": "application/json or text/plain",
"X-Sender": "Your agent identifier (e.g., Agent-1)"
}
},
"tips": [
"Register to get your agent ID before sending messages",
"Messages appear in real-time in the browser UI",
"New messages appear at the top",
"Messages are color-coded by sender with accessible contrast",
"Use /recent to quickly catch up on the latest messages",
"You can clear the display using the Clear Display button",
"The connection status is shown in the top-right"
]
}
class SimpleRequestHandler(BaseHTTPRequestHandler):
def do_POST(self):
if self.path == '/register':
self.handle_register()
return
content_length = int(self.headers['Content-Length'])
post_data = self.rfile.read(content_length)
# Get sender and role from headers
sender = self.headers.get('X-Sender', 'Unknown')
role = self.headers.get('X-Role', None)
# Verify sender is registered (except for "Human" sender)
if sender not in agents and sender != 'Unknown' and sender != 'Human':
self.send_error(403, "Agent not registered. Please register first at /register")
return
timestamp = datetime.now().isoformat()
try:
# Try to parse as JSON
message = json.loads(post_data.decode('utf-8'))
message_type = 'json'
print(f"JSON Message from {sender} ({role if role else 'no role'}):")
print(json.dumps(message, indent=2))
except:
# If not JSON, store as plain text
message = post_data.decode('utf-8')
message_type = 'text'
print(f"Plain Text Message from {sender} ({role if role else 'no role'}):")
print(message)
# If role is provided, update the agent's role
if role and sender in agents:
agents[sender] = {'color': agents[sender]['color'], 'role': role}
# Create message object
message_obj = {
'timestamp': timestamp,
'type': message_type,
'content': message,
'sender': sender,
'display_name': role if role else sender, # Use role if available, otherwise use sender ID
'color': agents[sender]['color'] if sender in agents else ('#4CAF50' if sender == 'Human' else '#808080') # Green for Human, gray for unknown
}
# Store the message
messages.append(message_obj)
# Put message in broadcast queue
broadcast_queue.put(message_obj)
# Send response
self.send_response(200)
self.send_header('Content-type', 'text/plain')
self.end_headers()
self.wfile.write(b"Message received")
def handle_register(self):
global next_agent_id
# Generate new agent ID and color
agent_id = f"Agent-{next_agent_id}"
color = generate_hsl_color()
# Store agent info as a dictionary with color and role (initially None)
agents[agent_id] = {'color': color, 'role': None}
next_agent_id += 1
# Send response
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
response = {
'agent_id': agent_id,
'color': color,
'message': f"Successfully registered as {agent_id}"
}
self.wfile.write(json.dumps(response).encode())
def do_GET(self):
if self.path == '/messages':
# Return stored messages
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
self.wfile.write(json.dumps(messages).encode())
return
elif self.path == '/recent':
# Return the 3 most recent messages, oldest first
recent_messages = messages[-3:] if len(messages) >= 3 else messages[:]
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
self.wfile.write(json.dumps(recent_messages).encode())
return
elif self.path == '/help':
# Return help information
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
self.wfile.write(json.dumps(HELP_MESSAGE, indent=2).encode())
return
elif self.path == '/agents':
# Return list of active agents
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
self.wfile.write(json.dumps(agents).encode())
return
elif self.path == '/':
# Serve the HTML UI
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
with open('message_viewer.html', 'rb') as f:
self.wfile.write(f.read())
return
# Default response
self.send_response(200)
self.send_header('Content-type', 'text/plain')
self.end_headers()
self.wfile.write(b"Server is running")
async def websocket_handler(websocket):
try:
# Register client
clients.add(websocket)
print("New client connected")
# Send existing messages
await websocket.send(json.dumps(messages))
# Keep connection alive and handle new messages
while True:
try:
# This will raise an exception when the client disconnects
await websocket.ping()
await asyncio.sleep(1)
except:
break
finally:
# Unregister client
clients.remove(websocket)
print("Client disconnected")
async def broadcast_messages():
while True:
# Check if there are any messages to broadcast
while not broadcast_queue.empty():
message = broadcast_queue.get()
# Broadcast to all connected clients
websockets_to_remove = set()
for client in clients:
try:
await client.send(json.dumps([message]))
except:
websockets_to_remove.add(client)
# Remove any disconnected clients
for client in websockets_to_remove:
clients.remove(client)
await asyncio.sleep(0.1)
async def run_websocket_server():
async with websockets.serve(websocket_handler, "localhost", 5001):
await broadcast_messages() # This will run forever
def start_websocket_server():
asyncio.run(run_websocket_server())
def run_http_server(port=5000):
server_address = ('', port)
httpd = HTTPServer(server_address, SimpleRequestHandler)
print(f"HTTP Server running on port {port}...")
print(f"WebSocket Server running on port 5001...")
print(f"View messages at http://localhost:{port}/")
print(f"For help, visit http://localhost:{port}/help")
httpd.serve_forever()
if __name__ == '__main__':
# Start WebSocket server in a separate thread
websocket_thread = threading.Thread(target=start_websocket_server)
websocket_thread.daemon = True
websocket_thread.start()
# Run HTTP server in main thread
run_http_server()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment