#!/usr/bin/env -S uv run --quiet --locked --script --no-cache # /// script # requires-python = ">=3.12" # dependencies = [ # "fastapi", # "redis", # "asyncio", # "uvicorn[standard]", # "jinja2", # ] # /// import asyncio from fastapi import FastAPI, Request, WebSocket, WebSocketDisconnect from fastapi.responses import HTMLResponse import json from redis import asyncio as redis from fastapi.templating import Jinja2Templates app = FastAPI() templates = Jinja2Templates(directory="templates") # Redis connection redis_client = redis.Redis(host="localhost", port=6379, decode_responses=True) new_button = """
""" reset_form = """
""" # Constants REDIS_MESSAGE_LIST = "chat_messages" WEBSOCKET_CHANNEL = "websocket_channel" # Store connected WebSockets connected_clients = {} color_levels = [400, 500, 600, 700, 800, 900] async def listen_to_redis(): level = 0 level_increment = 1 pubsub = redis_client.pubsub() await pubsub.subscribe(WEBSOCKET_CHANNEL) async for message in pubsub.listen(): print(message) if message["type"] == "message": data = message["data"] data = json.loads(data) if level == len(color_levels) - 1: level_increment = -1 elif level == 0: level_increment = 1 level += level_increment color_level = color_levels[level] # Send message to all connected clients for name, websocket in connected_clients.items(): html_data = "" if "chat_message" in data: if data.get("name") == name: html_data += f'
  • {data.get("name")}: {data.get("chat_message")}
  • ' else: html_data += f'
  • {data.get("name")}: {data.get("chat_message")}
  • ' if "notification" in data: html_data += f'
  • {data.get("notification")}
  • ' await websocket.send_text(html_data + new_button) @app.on_event("startup") async def startup(): asyncio.create_task(listen_to_redis()) chat_names = [ "Gregory", "Waylon", "John", "Jane", "Bob", "Alice", "Charlie", "Diana", "Ethan", "Fiona", "Grace", "Henry", "Isabella", "Jack", "Katherine", "Liam", "Mia", "Noah", "Olivia", "Oscar", "Penelope", "Quinn", "Riley", "Sophia", "Theo", "Uma", "Violet", "Walter", "Xavier", "Yvonne", "Zachary", ] @app.websocket("/ws") async def websocket_endpoint(websocket: WebSocket): await websocket.accept() import random name = random.choice(chat_names) connected_clients[name] = websocket data = { "name": name, "notification": f"{name} joined the chat", } data_str = json.dumps(data) await redis_client.publish(WEBSOCKET_CHANNEL, data_str) # Send previous messages to the new client messages = await redis_client.lrange(REDIS_MESSAGE_LIST, 0, -1) for msg in messages: msg_data = json.loads(msg) color_level = color_levels[0] # Use first color level for old messages html_data = "" if "chat_message" in msg_data: if msg_data.get("name") == name: html_data += f'
  • {msg_data.get("name")}: {msg_data.get("chat_message")}
  • ' else: html_data += f'
  • {msg_data.get("name")}: {msg_data.get("chat_message")}
  • ' if "notification" in msg_data: html_data += f'
  • {msg_data.get("notification")}
  • ' await websocket.send_text(html_data + new_button) try: while True: data = await websocket.receive_text() data = json.loads(data) data["name"] = name data_str = json.dumps(data) # Store message in Redis list await redis_client.rpush(REDIS_MESSAGE_LIST, data_str) # Publish message to Redis await redis_client.publish(WEBSOCKET_CHANNEL, data_str) await websocket.send_text(reset_form) except WebSocketDisconnect: connected_clients.pop(name) @app.get("/clear-chat") async def clear_chat(): await redis_client.delete(REDIS_MESSAGE_LIST) return """
    """ @app.get("/") async def root(request: Request): return templates.TemplateResponse("index.html", {"request": request}) @app.get("/null") async def root(request: Request): return HTMLResponse("") if __name__ == "__main__": import uvicorn import sys port = 8000 if len(sys.argv) > 1: port = int(sys.argv[1]) uvicorn.run("server:app", host="0.0.0.0", port=port, workers=6)