Chat widget for streamplace
chat-widget.html edited
169 lines 4.3 kB view raw
1<!doctype html> 2<html lang="en"> 3 <head> 4 <meta charset="UTF-8" /> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 6 <title>Chat Widget</title> 7 <style> 8 * { 9 margin: 0; 10 padding: 0; 11 box-sizing: border-box; 12 } 13 14 body { 15 background: transparent; 16 font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; 17 overflow: hidden; 18 } 19 20 #chat-container { 21 width: full; 22 height: fit-content; 23 display: flex; 24 flex-direction: column; 25 justify-content: flex-end; 26 background: rgba(0, 0, 0, 0.7); 27 padding: 4px; 28 font-size: 2rem; 29 } 30 31 .message { 32 padding: 2px; 33 } 34 35 .username { 36 font-weight: 600; 37 } 38 39 .message-text { 40 color: #ffffff; 41 word-wrap: break-word; 42 } 43 44 #status { 45 position: fixed; 46 top: 10px; 47 right: 10px; 48 padding: 8px 12px; 49 border-radius: 4px; 50 font-size: 16px; 51 font-weight: bold; 52 } 53 54 .connected { 55 display: none; 56 background: rgba(0, 255, 0, 0.2); 57 color: #00ff00; 58 border: 1px solid #00ff00; 59 } 60 61 .disconnected { 62 background: rgba(255, 0, 0, 0.2); 63 color: #ff0000; 64 border: 1px solid #ff0000; 65 } 66 67 .connecting { 68 background: rgba(255, 255, 0, 0.2); 69 color: #ffff00; 70 border: 1px solid #ffff00; 71 } 72 </style> 73 </head> 74 <body> 75 <div id="status" class="disconnected">Disconnected</div> 76 <div id="chat-container"></div> 77 78 <script> 79 const HANDLE = "juli.ee"; 80 const WEBSOCKET_URL = "wss://stream.place/api/websocket/" + HANDLE; 81 const MAX_MESSAGES = 15; 82 83 let ws = null; 84 const chatContainer = document.getElementById("chat-container"); 85 const statusElement = document.getElementById("status"); 86 const messages = []; 87 88 function updateStatus(status) { 89 statusElement.className = status; 90 statusElement.textContent = 91 status.charAt(0).toUpperCase() + status.slice(1); 92 } 93 94 function addMessage(username, text, color) { 95 const messageDiv = document.createElement("div"); 96 messageDiv.className = "message"; 97 messageDiv.innerHTML = ` 98<span class="username" style="color: rgb(${color.red}, ${color.green}, ${color.blue})">${escapeHtml(username)}</span><span class="message-text">: ${escapeHtml(text)}</span> 99 `; 100 101 chatContainer.appendChild(messageDiv); 102 messages.push(messageDiv); 103 104 // Remove old messages if exceeding max 105 if (messages.length > MAX_MESSAGES) { 106 const oldMessage = messages.shift(); 107 oldMessage.remove(); 108 } 109 110 // Auto-scroll 111 chatContainer.scrollTop = chatContainer.scrollHeight; 112 } 113 114 function escapeHtml(text) { 115 const div = document.createElement("div"); 116 div.textContent = text; 117 return div.innerHTML; 118 } 119 120 function connect() { 121 updateStatus("connecting"); 122 123 try { 124 ws = new WebSocket(WEBSOCKET_URL); 125 126 ws.onopen = () => { 127 console.log("WebSocket connected"); 128 updateStatus("connected"); 129 }; 130 131 ws.onmessage = (event) => { 132 try { 133 const data = JSON.parse(event.data); 134 135 if (data.$type === "place.stream.chat.defs#messageView") { 136 addMessage( 137 data.author.handle, 138 data.record.text, 139 data.chatProfile.color, 140 ); 141 } 142 } catch (e) { 143 console.error("Error parsing message:", e); 144 } 145 }; 146 147 ws.onerror = (error) => { 148 console.error("WebSocket error:", error); 149 updateStatus("disconnected"); 150 }; 151 152 ws.onclose = () => { 153 console.log("WebSocket disconnected"); 154 updateStatus("disconnected"); 155 156 // Auto-reconnect after 3 seconds 157 setTimeout(connect, 3000); 158 }; 159 } catch (e) { 160 console.error("Connection error:", e); 161 updateStatus("disconnected"); 162 setTimeout(connect, 3000); 163 } 164 } 165 166 connect(); 167 </script> 168 </body> 169</html>