--[[ WARNING: Heads up! This script has not been verified by ScriptBlox. Use at your own risk! ]] --// Global Chat GUI (2x smaller, heartbeat online tracking) local Players = game:GetService("Players") local HttpService = game:GetService("HttpService") local UserInputService = game:GetService("UserInputService") local LocalPlayer = Players.LocalPlayer local PlayerGui = LocalPlayer:WaitForChild("PlayerGui") local API_BASE = "https://mii-response-server--721843.replit.app/api" local JOIN_URL = API_BASE .. "/chat/join" local LEAVE_URL = API_BASE .. "/chat/leave" local SEND_URL = API_BASE .. "/chat/send" local MESSAGES_URL = API_BASE .. "/chat/messages" local ONLINE_URL = API_BASE .. "/chat/online" local HEARTBEAT_URL = API_BASE .. "/chat/heartbeat" local POLL_INTERVAL = 2 local ONLINE_INTERVAL = 5 local HEARTBEAT_INTERVAL = 10 local MAX_MESSAGE_LEN = 300 local function jsonDecodeSafe(str) local ok, r = pcall(function() return HttpService:JSONDecode(str) end) return ok and r or nil end local function jsonEncodeSafe(tbl) local ok, r = pcall(function() return HttpService:JSONEncode(tbl) end) return ok and r or nil end local function httpRequest(method, url, bodyTable) local body = bodyTable and jsonEncodeSafe(bodyTable) or nil if typeof(request) == "function" then local ok, res = pcall(function() return request({ Url = url, Method = method, Headers = body and { ["Content-Type"] = "application/json" } or {}, Body = body, }) end) if ok and res then local success = res.Success or (res.StatusCode and res.StatusCode >= 200 and res.StatusCode < 300) return success, res.Body or "" end end if method == "GET" then local ok, res = pcall(function() return game:HttpGet(url) end) return ok, res elseif method == "POST" then local ok, res = pcall(function() return game:HttpPost(url, body, "application/json") end) return ok, res end return false, "Unsupported method" end local function escapeRichText(text) text = tostring(text or "") text = text:gsub("&","&"):gsub("<","<"):gsub(">",">") return text end local function trim(text) return (tostring(text or ""):gsub("^%s*(.-)%s*$", "%1")) end local function formatTime(ts) local ok, r = pcall(function() return os.date("!%H:%M", math.floor(tonumber(ts) or os.time())) end) return ok and r or "--:--" end -- GUI local gui = Instance.new("ScreenGui") gui.Name = "GlobalChatGui" gui.ResetOnSpawn = false gui.IgnoreGuiInset = true gui.Parent = PlayerGui local main = Instance.new("Frame") main.AnchorPoint = Vector2.new(0.5, 0.5) main.Position = UDim2.new(0.5, 0, 0.5, 0) main.Size = UDim2.new(0.45, 0, 0.29, 0) main.BackgroundColor3 = Color3.fromRGB(20, 20, 24) main.BorderSizePixel = 0 main.Parent = gui local c = Instance.new("UICorner"); c.CornerRadius = UDim.new(0, 8); c.Parent = main local s = Instance.new("UIStroke"); s.Thickness = 1; s.Transparency = 0.5 s.Color = Color3.fromRGB(255,255,255); s.Parent = main local lim = Instance.new("UISizeConstraint") lim.MinSize = Vector2.new(140, 180); lim.MaxSize = Vector2.new(210, 280); lim.Parent = main local topBar = Instance.new("Frame") topBar.Size = UDim2.new(1, 0, 0, 20) topBar.BackgroundColor3 = Color3.fromRGB(28, 28, 34) topBar.BorderSizePixel = 0 topBar.Parent = main local tc = Instance.new("UICorner"); tc.CornerRadius = UDim.new(0, 8); tc.Parent = topBar local tm = Instance.new("Frame") tm.Size = UDim2.new(1, 0, 0, 8); tm.Position = UDim2.new(0, 0, 1, -8) tm.BackgroundColor3 = Color3.fromRGB(28,28,34); tm.BorderSizePixel = 0; tm.Parent = topBar local titleLbl = Instance.new("TextLabel") titleLbl.BackgroundTransparency = 1 titleLbl.Size = UDim2.new(1, -60, 1, 0); titleLbl.Position = UDim2.new(0, 7, 0, 0) titleLbl.Font = Enum.Font.GothamBold; titleLbl.Text = "Global Chat" titleLbl.TextColor3 = Color3.fromRGB(255,255,255); titleLbl.TextSize = 11 titleLbl.TextXAlignment = Enum.TextXAlignment.Left; titleLbl.Parent = topBar local onlineLabel = Instance.new("TextLabel") onlineLabel.BackgroundTransparency = 1 onlineLabel.Size = UDim2.new(0, 55, 1, 0); onlineLabel.Position = UDim2.new(1, -58, 0, 0) onlineLabel.Font = Enum.Font.Gotham; onlineLabel.Text = "Online: 0" onlineLabel.TextColor3 = Color3.fromRGB(200,200,200); onlineLabel.TextSize = 10 onlineLabel.TextXAlignment = Enum.TextXAlignment.Right; onlineLabel.Parent = topBar local statusLabel = Instance.new("TextLabel") statusLabel.BackgroundTransparency = 1 statusLabel.Size = UDim2.new(1, -14, 0, 12); statusLabel.Position = UDim2.new(0, 7, 0, 23) statusLabel.Font = Enum.Font.Gotham; statusLabel.Text = "Connecting..." statusLabel.TextColor3 = Color3.fromRGB(180,180,180); statusLabel.TextSize = 10 statusLabel.TextXAlignment = Enum.TextXAlignment.Left; statusLabel.Parent = main local messagesFrame = Instance.new("ScrollingFrame") messagesFrame.Size = UDim2.new(1, -14, 1, -66) messagesFrame.Position = UDim2.new(0, 7, 0, 38) messagesFrame.BackgroundColor3 = Color3.fromRGB(16,16,20) messagesFrame.BorderSizePixel = 0; messagesFrame.ScrollBarThickness = 3 messagesFrame.CanvasSize = UDim2.new(0,0,0,0) messagesFrame.AutomaticCanvasSize = Enum.AutomaticSize.Y messagesFrame.ScrollingDirection = Enum.ScrollingDirection.Y messagesFrame.Parent = main local mc = Instance.new("UICorner"); mc.CornerRadius = UDim.new(0, 7); mc.Parent = messagesFrame local mp = Instance.new("UIPadding") mp.PaddingTop = UDim.new(0,6); mp.PaddingBottom = UDim.new(0,6) mp.PaddingLeft = UDim.new(0,6); mp.PaddingRight = UDim.new(0,6); mp.Parent = messagesFrame local layout = Instance.new("UIListLayout") layout.Padding = UDim.new(0, 4); layout.SortOrder = Enum.SortOrder.LayoutOrder layout.Parent = messagesFrame local inputFrame = Instance.new("Frame") inputFrame.Size = UDim2.new(1, -14, 0, 22); inputFrame.Position = UDim2.new(0, 7, 1, -28) inputFrame.BackgroundTransparency = 1; inputFrame.Parent = main local textBox = Instance.new("TextBox") textBox.Size = UDim2.new(1, -52, 1, 0) textBox.BackgroundColor3 = Color3.fromRGB(28,28,34); textBox.BorderSizePixel = 0 textBox.ClearTextOnFocus = false; textBox.PlaceholderText = "Type a message..." textBox.Text = ""; textBox.Font = Enum.Font.Gotham; textBox.TextSize = 11 textBox.TextColor3 = Color3.fromRGB(255,255,255) textBox.PlaceholderColor3 = Color3.fromRGB(150,150,150) textBox.TextXAlignment = Enum.TextXAlignment.Left; textBox.Parent = inputFrame local tbc = Instance.new("UICorner"); tbc.CornerRadius = UDim.new(0,6); tbc.Parent = textBox local tbp = Instance.new("UIPadding") tbp.PaddingLeft = UDim.new(0,6); tbp.PaddingRight = UDim.new(0,6); tbp.Parent = textBox local sendButton = Instance.new("TextButton") sendButton.Size = UDim2.new(0, 46, 1, 0); sendButton.Position = UDim2.new(1, -46, 0, 0) sendButton.BackgroundColor3 = Color3.fromRGB(70,110,255); sendButton.BorderSizePixel = 0 sendButton.Text = "Send"; sendButton.Font = Enum.Font.GothamBold; sendButton.TextSize = 11 sendButton.TextColor3 = Color3.fromRGB(255,255,255); sendButton.Parent = inputFrame local sc = Instance.new("UICorner"); sc.CornerRadius = UDim.new(0,6); sc.Parent = sendButton local messageCount = 0 local function scrollToBottom() task.defer(function() messagesFrame.CanvasPosition = Vector2.new(0, math.huge) end) end local function addMessage(entry) if not entry then return end messageCount += 1 local username = escapeRichText(entry.username or "Unknown") local message = escapeRichText(entry.message or "") local timeStr = formatTime(entry.timestamp) local row = Instance.new("Frame") row.BackgroundTransparency = 1 row.Size = UDim2.new(1, 0, 0, 0) row.AutomaticSize = Enum.AutomaticSize.Y row.LayoutOrder = messageCount row.Parent = messagesFrame local label = Instance.new("TextLabel") label.BackgroundTransparency = 1 label.Size = UDim2.new(1, 0, 0, 0) label.AutomaticSize = Enum.AutomaticSize.Y label.Font = Enum.Font.Gotham; label.TextSize = 11 label.TextWrapped = true; label.RichText = true label.TextXAlignment = Enum.TextXAlignment.Left label.TextYAlignment = Enum.TextYAlignment.Top label.TextColor3 = Color3.fromRGB(240,240,240) label.Text = string.format( "[%s] %s: %s", timeStr, username, message ) label.Parent = row scrollToBottom() end local function setStatus(text, color) statusLabel.Text = text statusLabel.TextColor3 = color or Color3.fromRGB(180,180,180) end -- Drag local dragging, dragInput, dragStart, startPos = false, nil, nil, nil topBar.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then dragging = true; dragStart = input.Position; startPos = main.Position input.Changed:Connect(function() if input.UserInputState == Enum.UserInputState.End then dragging = false end end) end end) topBar.InputChanged:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch then dragInput = input end end) UserInputService.InputChanged:Connect(function(input) if dragging and input == dragInput then local delta = input.Position - dragStart main.Position = UDim2.new( startPos.X.Scale, startPos.X.Offset + delta.X, startPos.Y.Scale, startPos.Y.Offset + delta.Y ) end end) local lastId = 0 local sending = false local alive = true local function getMessages(afterId, limit) local url = string.format("%s?after=%d&limit=%d", MESSAGES_URL, afterId, limit) local ok, res = httpRequest("GET", url) if not ok then return nil end return jsonDecodeSafe(res) end local function refreshOnline() local ok, res = httpRequest("GET", ONLINE_URL) if not ok then return end local data = jsonDecodeSafe(res) if data and data.count ~= nil then onlineLabel.Text = "Online: " .. tostring(data.count) end end local function sendChatMessage() if sending then return end local text = trim(textBox.Text) if text == "" then return end if #text > MAX_MESSAGE_LEN then text = text:sub(1, MAX_MESSAGE_LEN) end sending = true; sendButton.Text = "..."; sendButton.Active = false local ok = httpRequest("POST", SEND_URL, { username = LocalPlayer.Name, userId = tostring(LocalPlayer.UserId), message = text, }) if ok then textBox.Text = ""; setStatus("Sent", Color3.fromRGB(120,255,160)) else setStatus("Send failed", Color3.fromRGB(255,120,120)) end sendButton.Text = "Send"; sendButton.Active = true; sending = false end sendButton.MouseButton1Click:Connect(sendChatMessage) textBox.FocusLost:Connect(function(enter) if enter then sendChatMessage() end end) -- Initial load + join task.spawn(function() httpRequest("POST", JOIN_URL, { username = LocalPlayer.Name, userId = tostring(LocalPlayer.UserId), }) refreshOnline() local data = getMessages(0, 50) if data and data.messages then table.sort(data.messages, function(a,b) return (tonumber(a.id) or 0) < (tonumber(b.id) or 0) end) for _, msg in ipairs(data.messages) do addMessage(msg) lastId = math.max(lastId, tonumber(msg.id) or 0) end scrollToBottom() end setStatus("Connected", Color3.fromRGB(120,255,160)) end) -- Poll messages task.spawn(function() while alive do task.wait(POLL_INTERVAL) local data = getMessages(lastId, 50) if data and data.messages then for _, msg in ipairs(data.messages) do local id = tonumber(msg.id) or 0 if id > lastId then addMessage(msg); lastId = id end end end end end) -- Refresh online count task.spawn(function() while alive do task.wait(ONLINE_INTERVAL) refreshOnline() end end) -- Heartbeat — keeps this player in the online list task.spawn(function() while alive do task.wait(HEARTBEAT_INTERVAL) httpRequest("POST", HEARTBEAT_URL, { userId = tostring(LocalPlayer.UserId) }) end end) -- Leave on close pcall(function() game:BindToClose(function() alive = false httpRequest("POST", LEAVE_URL, { username = LocalPlayer.Name, userId = tostring(LocalPlayer.UserId), }) end) end)