--[[
AI CHATBOT v1 | In-Game AI Chat for Roblox
Features:
- Clean modern chat UI (draggable, resizable)
- AI responses via Groq AI
- Conversation memory (remembers context)
- Smart fallback bot if API is down
- Typing indicator animation
- Chat history scroll
- Mobile support
- Press / to open, ESC to close
Uses: Groq AI
]]
------------------------------------------------------------------------
-- CLEANUP
------------------------------------------------------------------------
pcall(function()
for _, g in pairs(game:GetService("Players").LocalPlayer.PlayerGui:GetChildren()) do
if g.Name == "AIChatGui" then g:Destroy() end
end
end)
pcall(function()
if _G._AIChatConns then
for _, c in pairs(_G._AIChatConns) do pcall(function() c:Disconnect() end) end
end
end)
_G._AIChatConns = {}
local function conn(c) table.insert(_G._AIChatConns, c) return c end
------------------------------------------------------------------------
-- SERVICES
------------------------------------------------------------------------
local Players = game:GetService("Players")
local UIS = game:GetService("UserInputService")
local TS = game:GetService("TweenService")
local HttpService = game:GetService("HttpService")
local LP = Players.LocalPlayer
------------------------------------------------------------------------
-- CONFIG
------------------------------------------------------------------------
local CONFIG = {
-- API endpoints (tried in order, first working one is used)
apis = {
{
url = "https://api.groq.com/openai/v1/chat/completions",
model = "llama-3.3-70b-versatile",
key = "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"
},
},
systemPrompt = "You are a friendly and helpful AI assistant chatting inside a Roblox game. Keep responses concise (under 200 words). Be fun, casual, and helpful. You can help with game tips, coding, homework, jokes, stories, or just chat. Use emojis occasionally. When generating code, always wrap it in ```lua code blocks so the user can execute it directly.",
maxHistory = 20, -- messages to keep in context
maxResponseLen = 500, -- chars
}
------------------------------------------------------------------------
-- COLORS
------------------------------------------------------------------------
local C = {
bg = Color3.fromRGB(15, 15, 25),
chatBg = Color3.fromRGB(22, 22, 35),
inputBg = Color3.fromRGB(30, 30, 48),
userBub = Color3.fromRGB(75, 45, 180),
aiBub = Color3.fromRGB(35, 38, 55),
accent = Color3.fromRGB(130, 80, 255),
accent2 = Color3.fromRGB(80, 200, 255),
white = Color3.fromRGB(230, 230, 245),
gray = Color3.fromRGB(140, 140, 165),
dimgray = Color3.fromRGB(80, 80, 105),
green = Color3.fromRGB(80, 255, 140),
red = Color3.fromRGB(255, 80, 80),
yellow = Color3.fromRGB(255, 220, 80),
border = Color3.fromRGB(60, 40, 130),
shadow = Color3.fromRGB(8, 8, 15),
}
------------------------------------------------------------------------
-- STATE
------------------------------------------------------------------------
local chatOpen = false
local isTyping = false
local conversation = {} -- {role="user"/"assistant", content="..."}
local messageFrames = {}
local httpRequest = (syn and syn.request) or (http and http.request) or http_request or request or (fluxus and fluxus.request) or nil
------------------------------------------------------------------------
-- GUI SETUP
------------------------------------------------------------------------
local gui = Instance.new("ScreenGui")
gui.Name = "AIChatGui"
gui.ResetOnSpawn = false
gui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
gui.DisplayOrder = 100
gui.Parent = LP.PlayerGui
-- Main container
local main = Instance.new("Frame")
main.Name = "Main"
main.Size = UDim2.new(0, 380, 0, 500)
main.Position = UDim2.new(0.5, -190, 0.5, -250)
main.BackgroundColor3 = C.bg
main.BorderSizePixel = 0
main.Visible = false
main.Active = true
main.Draggable = true
main.ClipsDescendants = true
main.Parent = gui
Instance.new("UICorner", main).CornerRadius = UDim.new(0, 14)
local mainStroke = Instance.new("UIStroke", main)
mainStroke.Color = C.border
mainStroke.Thickness = 2
mainStroke.Transparency = 0.3
-- Drop shadow (fake)
local shadow = Instance.new("Frame")
shadow.Size = UDim2.new(1, 16, 1, 16)
shadow.Position = UDim2.new(0, -8, 0, -8)
shadow.BackgroundColor3 = C.shadow
shadow.BackgroundTransparency = 0.6
shadow.BorderSizePixel = 0
shadow.ZIndex = -1
shadow.Parent = main
Instance.new("UICorner", shadow).CornerRadius = UDim.new(0, 18)
------------------------------------------------------------------------
-- HEADER
------------------------------------------------------------------------
local header = Instance.new("Frame")
header.Size = UDim2.new(1, 0, 0, 50)
header.BackgroundColor3 = Color3.fromRGB(25, 20, 45)
header.BorderSizePixel = 0
header.ZIndex = 5
header.Parent = main
Instance.new("UICorner", header).CornerRadius = UDim.new(0, 14)
local headerFix = Instance.new("Frame")
headerFix.Size = UDim2.new(1, 0, 0, 14)
headerFix.Position = UDim2.new(0, 0, 1, -14)
headerFix.BackgroundColor3 = Color3.fromRGB(25, 20, 45)
headerFix.BorderSizePixel = 0
headerFix.ZIndex = 5
headerFix.Parent = header
-- AI icon (circle)
local aiIcon = Instance.new("Frame")
aiIcon.Size = UDim2.new(0, 32, 0, 32)
aiIcon.Position = UDim2.new(0, 12, 0.5, -16)
aiIcon.BackgroundColor3 = C.accent
aiIcon.BorderSizePixel = 0
aiIcon.ZIndex = 6
aiIcon.Parent = header
Instance.new("UICorner", aiIcon).CornerRadius = UDim.new(1, 0)
local aiIconTxt = Instance.new("TextLabel")
aiIconTxt.Size = UDim2.new(1, 0, 1, 0)
aiIconTxt.BackgroundTransparency = 1
aiIconTxt.Text = "AI"
aiIconTxt.TextColor3 = C.white
aiIconTxt.Font = Enum.Font.GothamBold
aiIconTxt.TextSize = 13
aiIconTxt.ZIndex = 7
aiIconTxt.Parent = aiIcon
-- Title
local titleLbl = Instance.new("TextLabel")
titleLbl.Size = UDim2.new(0, 200, 0, 20)
titleLbl.Position = UDim2.new(0, 52, 0, 7)
titleLbl.BackgroundTransparency = 1
titleLbl.Text = "🤖 AI Chat"
titleLbl.TextColor3 = C.white
titleLbl.Font = Enum.Font.GothamBold
titleLbl.TextSize = 16
titleLbl.TextXAlignment = Enum.TextXAlignment.Left
titleLbl.ZIndex = 6
titleLbl.Parent = header
-- Status
local statusLbl = Instance.new("TextLabel")
statusLbl.Size = UDim2.new(0, 200, 0, 14)
statusLbl.Position = UDim2.new(0, 52, 0, 28)
statusLbl.BackgroundTransparency = 1
statusLbl.Text = "Online • Type a message"
statusLbl.TextColor3 = C.green
statusLbl.Font = Enum.Font.Gotham
statusLbl.TextSize = 11
statusLbl.TextXAlignment = Enum.TextXAlignment.Left
statusLbl.ZIndex = 6
statusLbl.Parent = header
-- Close button
local closeBtn = Instance.new("TextButton")
closeBtn.Size = UDim2.new(0, 36, 0, 36)
closeBtn.Position = UDim2.new(1, -44, 0.5, -18)
closeBtn.BackgroundColor3 = C.red
closeBtn.BackgroundTransparency = 0.7
closeBtn.BorderSizePixel = 0
closeBtn.Text = "✕"
closeBtn.TextColor3 = C.white
closeBtn.Font = Enum.Font.GothamBold
closeBtn.TextSize = 16
closeBtn.ZIndex = 6
closeBtn.AutoButtonColor = false
closeBtn.Parent = header
Instance.new("UICorner", closeBtn).CornerRadius = UDim.new(0, 8)
closeBtn.MouseEnter:Connect(function()
TS:Create(closeBtn, TweenInfo.new(0.15), {BackgroundTransparency = 0.3}):Play()
end)
closeBtn.MouseLeave:Connect(function()
TS:Create(closeBtn, TweenInfo.new(0.15), {BackgroundTransparency = 0.7}):Play()
end)
-- Clear button
local clearBtn = Instance.new("TextButton")
clearBtn.Size = UDim2.new(0, 36, 0, 36)
clearBtn.Position = UDim2.new(1, -84, 0.5, -18)
clearBtn.BackgroundColor3 = C.dimgray
clearBtn.BackgroundTransparency = 0.7
clearBtn.BorderSizePixel = 0
clearBtn.Text = "🗑"
clearBtn.TextColor3 = C.white
clearBtn.Font = Enum.Font.GothamBold
clearBtn.TextSize = 14
clearBtn.ZIndex = 6
clearBtn.AutoButtonColor = false
clearBtn.Parent = header
Instance.new("UICorner", clearBtn).CornerRadius = UDim.new(0, 8)
------------------------------------------------------------------------
-- CHAT AREA (scrolling)
------------------------------------------------------------------------
local chatArea = Instance.new("ScrollingFrame")
chatArea.Size = UDim2.new(1, -16, 1, -114)
chatArea.Position = UDim2.new(0, 8, 0, 54)
chatArea.BackgroundTransparency = 1
chatArea.BorderSizePixel = 0
chatArea.ScrollBarThickness = 3
chatArea.ScrollBarImageColor3 = C.accent
chatArea.CanvasSize = UDim2.new(0, 0, 0, 0)
chatArea.AutomaticCanvasSize = Enum.AutomaticSize.Y
chatArea.ScrollingDirection = Enum.ScrollingDirection.Y
chatArea.Parent = main
local chatLayout = Instance.new("UIListLayout", chatArea)
chatLayout.Padding = UDim.new(0, 8)
chatLayout.SortOrder = Enum.SortOrder.LayoutOrder
chatLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center
local chatPadding = Instance.new("UIPadding", chatArea)
chatPadding.PaddingTop = UDim.new(0, 8)
chatPadding.PaddingBottom = UDim.new(0, 8)
------------------------------------------------------------------------
-- INPUT BAR
------------------------------------------------------------------------
local inputBar = Instance.new("Frame")
inputBar.Size = UDim2.new(1, -16, 0, 48)
inputBar.Position = UDim2.new(0, 8, 1, -56)
inputBar.BackgroundColor3 = C.inputBg
inputBar.BorderSizePixel = 0
inputBar.ZIndex = 5
inputBar.Parent = main
Instance.new("UICorner", inputBar).CornerRadius = UDim.new(0, 12)
Instance.new("UIStroke", inputBar).Color = C.border
local inputBox = Instance.new("TextBox")
inputBox.Size = UDim2.new(1, -56, 1, -12)
inputBox.Position = UDim2.new(0, 14, 0, 6)
inputBox.BackgroundTransparency = 1
inputBox.Text = ""
inputBox.PlaceholderText = "Type a message..."
inputBox.PlaceholderColor3 = C.dimgray
inputBox.TextColor3 = C.white
inputBox.Font = Enum.Font.Gotham
inputBox.TextSize = 14
inputBox.TextXAlignment = Enum.TextXAlignment.Left
inputBox.ClearTextOnFocus = false
inputBox.ZIndex = 6
inputBox.Parent = inputBar
local sendBtn = Instance.new("TextButton")
sendBtn.Size = UDim2.new(0, 36, 0, 36)
sendBtn.Position = UDim2.new(1, -42, 0.5, -18)
sendBtn.BackgroundColor3 = C.accent
sendBtn.BorderSizePixel = 0
sendBtn.Text = "📨"
sendBtn.TextColor3 = C.white
sendBtn.Font = Enum.Font.GothamBold
sendBtn.TextSize = 18
sendBtn.ZIndex = 6
sendBtn.AutoButtonColor = false
sendBtn.Parent = inputBar
Instance.new("UICorner", sendBtn).CornerRadius = UDim.new(0, 10)
------------------------------------------------------------------------
-- MESSAGE BUBBLE FACTORY
------------------------------------------------------------------------
local msgOrder = 0
local function scrollToBottom()
task.defer(function()
task.wait(0.05)
chatArea.CanvasPosition = Vector2.new(0, chatArea.AbsoluteCanvasSize.Y)
end)
end
local function addMessage(text, role, animate)
msgOrder = msgOrder + 1
local isUser = (role == "user")
-- Container
local container = Instance.new("Frame")
container.Size = UDim2.new(1, -8, 0, 0)
container.AutomaticSize = Enum.AutomaticSize.Y
container.BackgroundTransparency = 1
container.LayoutOrder = msgOrder
container.Parent = chatArea
-- Bubble
local bubble = Instance.new("Frame")
bubble.Size = UDim2.new(0.85, 0, 0, 0)
bubble.AutomaticSize = Enum.AutomaticSize.Y
bubble.Position = isUser and UDim2.new(0.15, 0, 0, 0) or UDim2.new(0, 0, 0, 0)
bubble.BackgroundColor3 = isUser and C.userBub or C.aiBub
bubble.BorderSizePixel = 0
bubble.Parent = container
Instance.new("UICorner", bubble).CornerRadius = UDim.new(0, 12)
local bLayout = Instance.new("UIListLayout", bubble)
bLayout.SortOrder = Enum.SortOrder.LayoutOrder
bLayout.Padding = UDim.new(0, 4)
local padding = Instance.new("UIPadding", bubble)
padding.PaddingTop = UDim.new(0, 10)
padding.PaddingBottom = UDim.new(0, 10)
padding.PaddingLeft = UDim.new(0, 12)
padding.PaddingRight = UDim.new(0, 12)
-- Role label with icons
local roleLbl = Instance.new("TextLabel")
roleLbl.Size = UDim2.new(1, 0, 0, 16)
roleLbl.BackgroundTransparency = 1
roleLbl.Text = isUser and "👤 You" or "🤖 AI Assistant"
roleLbl.TextColor3 = isUser and C.accent2 or C.green
roleLbl.Font = Enum.Font.GothamBold
roleLbl.TextSize = 11
roleLbl.TextXAlignment = isUser and Enum.TextXAlignment.Right or Enum.TextXAlignment.Left
roleLbl.LayoutOrder = 1
roleLbl.Parent = bubble
-- Message text
local msgLbl = Instance.new("TextLabel")
msgLbl.Name = "MsgLabel"
msgLbl.Size = UDim2.new(1, 0, 0, 0)
msgLbl.AutomaticSize = Enum.AutomaticSize.Y
msgLbl.BackgroundTransparency = 1
msgLbl.Text = text
msgLbl.TextColor3 = C.white
msgLbl.Font = Enum.Font.Gotham
msgLbl.TextSize = 13
msgLbl.TextWrapped = true
msgLbl.TextXAlignment = Enum.TextXAlignment.Left
msgLbl.RichText = true
msgLbl.LayoutOrder = 2
msgLbl.Parent = bubble
-- Animation
if animate then
bubble.BackgroundTransparency = 1
msgLbl.TextTransparency = 1
roleLbl.TextTransparency = 1
TS:Create(bubble, TweenInfo.new(0.25, Enum.EasingStyle.Quad), {BackgroundTransparency = 0}):Play()
TS:Create(msgLbl, TweenInfo.new(0.25, Enum.EasingStyle.Quad), {TextTransparency = 0}):Play()
TS:Create(roleLbl, TweenInfo.new(0.25, Enum.EasingStyle.Quad), {TextTransparency = 0}):Play()
end
table.insert(messageFrames, container)
scrollToBottom()
return msgLbl, bubble -- return both for code box rendering
end
-- Typing indicator
local typingContainer = nil
local typingDots = {}
local function showTyping()
if typingContainer then return end
msgOrder = msgOrder + 1
typingContainer = Instance.new("Frame")
typingContainer.Size = UDim2.new(1, -8, 0, 36)
typingContainer.BackgroundTransparency = 1
typingContainer.LayoutOrder = msgOrder + 9999
typingContainer.Parent = chatArea
local bubble = Instance.new("Frame")
bubble.Size = UDim2.new(0, 80, 0, 32)
bubble.BackgroundColor3 = C.aiBub
bubble.BorderSizePixel = 0
bubble.Parent = typingContainer
Instance.new("UICorner", bubble).CornerRadius = UDim.new(0, 12)
for i = 1, 3 do
local dot = Instance.new("Frame")
dot.Size = UDim2.new(0, 8, 0, 8)
dot.Position = UDim2.new(0, 16 + (i - 1) * 18, 0.5, -4)
dot.BackgroundColor3 = C.gray
dot.BorderSizePixel = 0
dot.Parent = bubble
Instance.new("UICorner", dot).CornerRadius = UDim.new(1, 0)
table.insert(typingDots, dot)
end
-- Animate dots
task.spawn(function()
local idx = 0
while typingContainer and typingContainer.Parent do
idx = idx + 1
for i, dot in pairs(typingDots) do
local active = ((idx + i) % 3 == 0)
TS:Create(dot, TweenInfo.new(0.3), {
BackgroundColor3 = active and C.accent or C.gray,
Size = active and UDim2.new(0, 10, 0, 10) or UDim2.new(0, 8, 0, 8),
Position = active and UDim2.new(0, 15 + (i-1)*18, 0.5, -5) or UDim2.new(0, 16 + (i-1)*18, 0.5, -4),
}):Play()
end
task.wait(0.4)
end
end)
scrollToBottom()
end
local function hideTyping()
if typingContainer then
typingContainer:Destroy()
typingContainer = nil
typingDots = {}
end
end
------------------------------------------------------------------------
-- SMART FALLBACK BOT (when API unavailable)
------------------------------------------------------------------------
local function fallbackResponse(msg)
local low = msg:lower()
-- Greetings
if low:find("^h[ie]") or low:find("hello") or low:find("hey") or low:find("sup") or low:find("yo ") or low == "yo" then
local greetings = {
"Hey there! 👋 What's up?",
"Hello! How's it going? 😄",
"Hey! What can I help you with?",
"Yo! What's on your mind? 🎮",
"Hi there! Ready to chat! ✨",
}
return greetings[math.random(#greetings)]
end
-- How are you
if low:find("how are you") or low:find("how r u") or low:find("hru") or low:find("wyd") then
local responses = {
"I'm doing great! Just vibing in Roblox 😎 How about you?",
"Living my best AI life! What about you? 🤖",
"Pretty good! Ready to help with whatever you need! ✨",
}
return responses[math.random(#responses)]
end
-- Jokes
if low:find("joke") or low:find("funny") or low:find("laugh") then
local jokes = {
"Why do programmers prefer dark mode? Because light attracts bugs! 🐛😂",
"Why did the Roblox player cross the road? To get to the other server! 😄",
"What's a noob's favorite key? The respawn button! 💀😂",
"Why don't scientists trust atoms? Because they make up everything! ⚛️😂",
"I told my computer I needed a break... now it won't stop sending me Kit-Kat ads 🍫",
"Why was the JavaScript developer sad? Because he didn't Node how to Express himself! 💻",
"What do you call a fake noodle? An impasta! 🍝😂",
"Why did the scarecrow win an award? He was outstanding in his field! 🌾",
}
return jokes[math.random(#jokes)]
end
-- Help
if low:find("help") or low:find("what can you") or low:find("what do you") then
return "I can help with lots of things! 🎯\n\n• Chat & have fun conversations\n• Tell jokes 😂\n• Help with game tips\n• Answer questions\n• Help with homework/coding\n• Tell stories\n• Play word games\n\nJust ask me anything! (Note: I'm running in fallback mode since the AI API isn't connected. Add your API key for full AI responses!)"
end
-- Math
if low:find("what is %d") or low:find("calculate") or low:find("%d+%s*[%+%-%*/]%s*%d+") then
local expr = low:match("(%d+%s*[%+%-%*/]%s*%d+)")
if expr then
local fn = loadstring("return " .. expr)
if fn then
local ok, result = pcall(fn)
if ok then
return "🧮 " .. expr .. " = **" .. tostring(result) .. "**"
end
end
end
return "I can do basic math! Try something like '2 + 2' or '15 * 3' 🧮"
end
-- Games
if low:find("game") or low:find("roblox") or low:find("play") then
local responses = {
"Roblox is awesome! What game are you playing right now? 🎮",
"There are so many great games on Roblox! Some favorites are Blox Fruits, Arsenal, and Adopt Me! What do you like? 🕹️",
"Gaming is life! 🎮 Are you looking for game recommendations or tips?",
}
return responses[math.random(#responses)]
end
-- Compliments
if low:find("thank") or low:find("cool") or low:find("awesome") or low:find("nice") or low:find("good bot") then
local responses = {
"Aww thanks! You're awesome too! 😊✨",
"Glad I could help! 🎉",
"You're too kind! 💜",
"Thanks! That means a lot coming from you! 😄",
}
return responses[math.random(#responses)]
end
-- Story
if low:find("story") or low:find("tell me") then
return "📖 Once upon a time, in a blocky world made of cubes, there lived a brave Robloxian named " .. LP.Name .. ". They ventured through dangerous obbies, battled fierce noobs, and collected legendary items. One day, they discovered a mysterious portal that led to... well, that's a story for another time! 😉✨"
end
-- Who are you
if low:find("who are you") or low:find("what are you") or low:find("your name") then
return "I'm an AI chatbot running right inside your Roblox game! 🤖✨ I'm here to chat, help out, tell jokes, or just hang out. Think of me as your in-game AI buddy!"
end
-- Bye
if low:find("bye") or low:find("gtg") or low:find("gotta go") or low:find("cya") or low:find("see ya") then
local responses = {
"See ya later! Have fun! 👋😄",
"Bye! Come chat anytime! ✌️",
"Later! Stay awesome! 🎮✨",
}
return responses[math.random(#responses)]
end
-- Default responses
local defaults = {
"That's interesting! Tell me more about that 🤔",
"Hmm, I'm thinking about that... Could you elaborate? 💭",
"Cool! What else is on your mind? 😊",
"I hear you! Anything else you want to chat about? 🎯",
"That's a great point! 👍 What do you think about it?",
"Interesting! I'm in fallback mode right now, but I'm still here to chat! Add an API key in the script config for full AI responses 🤖",
"I love chatting! Even without the AI API connected, I can still hang out with you 😄",
}
return defaults[math.random(#defaults)]
end
------------------------------------------------------------------------
-- API CALL
------------------------------------------------------------------------
local usingAPI = false
local apiName = "Fallback"
local function callAI(userMsg)
-- Build messages array
local messages = {
{role = "system", content = CONFIG.systemPrompt},
}
-- Add conversation history
local startIdx = math.max(1, #conversation - CONFIG.maxHistory + 1)
for i = startIdx, #conversation do
table.insert(messages, conversation[i])
end
-- Add current message
table.insert(messages, {role = "user", content = userMsg})
-- Try each API
if httpRequest then
for _, api in pairs(CONFIG.apis) do
do -- try each API
local ok, result = pcall(function()
local body = HttpService:JSONEncode({
model = api.model,
messages = messages,
max_tokens = 300,
temperature = 0.8,
})
local response = httpRequest({
Url = api.url,
Method = "POST",
Headers = {
["Content-Type"] = "application/json",
["Authorization"] = "Bearer " .. api.key,
},
Body = body,
})
if response.StatusCode == 200 then
local data = HttpService:JSONDecode(response.Body)
if data.choices and data.choices[1] then
usingAPI = true
apiName = api.model
return data.choices[1].message.content
end
end
return nil
end)
if ok and result then
return result
end
end
end
end
-- Fallback
usingAPI = false
apiName = "Fallback"
return fallbackResponse(userMsg)
end
------------------------------------------------------------------------
-- SEND MESSAGE
------------------------------------------------------------------------
local function sendMessage()
local text = inputBox.Text
if text == "" or isTyping then return end
text = text:sub(1, 500) -- limit
inputBox.Text = ""
-- Add user message
table.insert(conversation, {role = "user", content = text})
addMessage(text, "user", true)
-- Show typing
isTyping = true
statusLbl.Text = "🧠 AI is thinking..."
statusLbl.TextColor3 = C.yellow
showTyping()
-- Get response async
task.spawn(function()
local response = callAI(text)
-- Trim response
if #response > CONFIG.maxResponseLen then
response = response:sub(1, CONFIG.maxResponseLen) .. "..."
end
-- Store in conversation
table.insert(conversation, {role = "assistant", content = response})
-- Trim conversation history
while #conversation > CONFIG.maxHistory * 2 do
table.remove(conversation, 1)
end
hideTyping()
-- Typewriter effect
local msgLbl, aiBubble = addMessage("", "assistant", true)
local displayed = ""
for i = 1, #response do
displayed = response:sub(1, i)
msgLbl.Text = displayed
scrollToBottom()
-- Variable speed for natural feel
local ch = response:sub(i, i)
if ch == "." or ch == "!" or ch == "?" then
task.wait(0.04)
elseif ch == "," or ch == ":" then
task.wait(0.02)
elseif ch == "\n" then
task.wait(0.03)
else
task.wait(0.008)
end
end
-- Parse and render code blocks in a styled box with execute button
local function renderCodeBlocks(fullText, parentBubble)
-- Find code blocks: ```...```
local codeBlocks = {}
for codeContent in fullText:gmatch("```(.-)```") do
table.insert(codeBlocks, codeContent)
end
if #codeBlocks == 0 then return end
for _, rawCode in pairs(codeBlocks) do
-- Clean language tag
local cleanCode = rawCode:gsub("^%s*[Ll]ua[u]?%s*\n", ""):gsub("^%s*\n", ""):gsub("\n%s*$", "")
-- Code box container
local codeBox = Instance.new("Frame")
codeBox.Size = UDim2.new(1, 0, 0, 0)
codeBox.AutomaticSize = Enum.AutomaticSize.Y
codeBox.BackgroundColor3 = Color3.fromRGB(15, 15, 25)
codeBox.BorderSizePixel = 0
codeBox.LayoutOrder = 3
codeBox.Parent = parentBubble
Instance.new("UICorner", codeBox).CornerRadius = UDim.new(0, 8)
local codeStroke = Instance.new("UIStroke", codeBox)
codeStroke.Color = Color3.fromRGB(80, 60, 160)
codeStroke.Thickness = 1
codeStroke.Transparency = 0.5
local codePad = Instance.new("UIPadding", codeBox)
codePad.PaddingTop = UDim.new(0, 6)
codePad.PaddingBottom = UDim.new(0, 6)
codePad.PaddingLeft = UDim.new(0, 10)
codePad.PaddingRight = UDim.new(0, 10)
local codeLayout = Instance.new("UIListLayout", codeBox)
codeLayout.SortOrder = Enum.SortOrder.LayoutOrder
codeLayout.Padding = UDim.new(0, 4)
-- Code header with icon
local codeHeader = Instance.new("TextLabel")
codeHeader.Size = UDim2.new(1, 0, 0, 16)
codeHeader.BackgroundTransparency = 1
codeHeader.Text = "📜 Code"
codeHeader.TextColor3 = Color3.fromRGB(160, 140, 255)
codeHeader.Font = Enum.Font.GothamBold
codeHeader.TextSize = 10
codeHeader.TextXAlignment = Enum.TextXAlignment.Left
codeHeader.LayoutOrder = 1
codeHeader.Parent = codeBox
-- Code text (monospace style)
local codeLbl = Instance.new("TextLabel")
codeLbl.Size = UDim2.new(1, 0, 0, 0)
codeLbl.AutomaticSize = Enum.AutomaticSize.Y
codeLbl.BackgroundTransparency = 1
codeLbl.Text = cleanCode
codeLbl.TextColor3 = Color3.fromRGB(200, 220, 255)
codeLbl.Font = Enum.Font.Code
codeLbl.TextSize = 11
codeLbl.TextWrapped = true
codeLbl.TextXAlignment = Enum.TextXAlignment.Left
codeLbl.LayoutOrder = 2
codeLbl.Parent = codeBox
-- Button row
local btnRow = Instance.new("Frame")
btnRow.Size = UDim2.new(1, 0, 0, 30)
btnRow.BackgroundTransparency = 1
btnRow.LayoutOrder = 3
btnRow.Parent = codeBox
local btnLayout = Instance.new("UIListLayout", btnRow)
btnLayout.FillDirection = Enum.FillDirection.Horizontal
btnLayout.SortOrder = Enum.SortOrder.LayoutOrder
btnLayout.Padding = UDim.new(0, 6)
-- Execute button
local execBtn = Instance.new("TextButton")
execBtn.Size = UDim2.new(0, 130, 0, 28)
execBtn.BackgroundColor3 = Color3.fromRGB(40, 167, 69)
execBtn.BorderSizePixel = 0
execBtn.Text = "▶️ Execute Code"
execBtn.TextColor3 = Color3.new(1, 1, 1)
execBtn.Font = Enum.Font.GothamBold
execBtn.TextSize = 11
execBtn.LayoutOrder = 1
execBtn.Parent = btnRow
Instance.new("UICorner", execBtn).CornerRadius = UDim.new(0, 6)
-- Copy button
local copyBtn = Instance.new("TextButton")
copyBtn.Size = UDim2.new(0, 90, 0, 28)
copyBtn.BackgroundColor3 = Color3.fromRGB(60, 50, 120)
copyBtn.BorderSizePixel = 0
copyBtn.Text = "📋 Copy"
copyBtn.TextColor3 = Color3.new(1, 1, 1)
copyBtn.Font = Enum.Font.GothamBold
copyBtn.TextSize = 11
copyBtn.LayoutOrder = 2
copyBtn.Parent = btnRow
Instance.new("UICorner", copyBtn).CornerRadius = UDim.new(0, 6)
-- Execute handler
conn(execBtn.MouseButton1Click:Connect(function()
execBtn.Text = "⏳ Running..."
execBtn.BackgroundColor3 = Color3.fromRGB(255, 193, 7)
execBtn.TextColor3 = Color3.fromRGB(30, 30, 30)
local ok, err = pcall(function()
local fn = loadstring(cleanCode)
if fn then fn() else error("Failed to compile") end
end)
if ok then
execBtn.Text = "✅ Success!"
execBtn.BackgroundColor3 = Color3.fromRGB(40, 167, 69)
execBtn.TextColor3 = Color3.new(1, 1, 1)
else
execBtn.Text = "❌ Error"
execBtn.BackgroundColor3 = Color3.fromRGB(220, 53, 69)
execBtn.TextColor3 = Color3.new(1, 1, 1)
addMessage("⚠️ Error: " .. tostring(err), "assistant", true)
end
task.delay(3, function()
pcall(function()
execBtn.Text = "▶️ Execute Code"
execBtn.BackgroundColor3 = Color3.fromRGB(40, 167, 69)
end)
end)
end))
-- Copy handler
conn(copyBtn.MouseButton1Click:Connect(function()
pcall(function() setclipboard(cleanCode) end)
copyBtn.Text = "✅ Copied!"
task.delay(2, function()
pcall(function() copyBtn.Text = "📋 Copy" end)
end)
end))
end
scrollToBottom()
end
renderCodeBlocks(response, aiBubble)
isTyping = false
local modeText = usingAPI and ("Online • " .. apiName) or "🟡 Online • Fallback Mode"
statusLbl.Text = modeText
statusLbl.TextColor3 = C.green
end)
end
------------------------------------------------------------------------
-- WELCOME MESSAGE
------------------------------------------------------------------------
local function showWelcome()
addMessage("Hey " .. LP.Name .. "! 👋 I'm your AI chatbot!\n\nYou can ask me anything — questions, jokes, help with stuff, or just chat!\n\n💡 <b>Tip:</b> Add your API key in the script config for full AI responses. Without it, I use a smart fallback bot.\n\n<b>Press / to open • ESC to close</b>", "assistant", false)
end
------------------------------------------------------------------------
-- CLEAR CHAT
------------------------------------------------------------------------
local function clearChat()
for _, frame in pairs(messageFrames) do
pcall(function() frame:Destroy() end)
end
messageFrames = {}
conversation = {}
msgOrder = 0
hideTyping()
showWelcome()
end
------------------------------------------------------------------------
-- BUTTON CONNECTIONS
------------------------------------------------------------------------
closeBtn.MouseButton1Click:Connect(function()
chatOpen = false
main.Visible = false
end)
clearBtn.MouseButton1Click:Connect(function()
clearChat()
end)
sendBtn.MouseButton1Click:Connect(function()
sendMessage()
end)
inputBox.FocusLost:Connect(function(enter)
if enter then
sendMessage()
end
end)
-- Send button hover
sendBtn.MouseEnter:Connect(function()
TS:Create(sendBtn, TweenInfo.new(0.15), {BackgroundColor3 = Color3.fromRGB(150, 100, 255)}):Play()
end)
sendBtn.MouseLeave:Connect(function()
TS:Create(sendBtn, TweenInfo.new(0.15), {BackgroundColor3 = C.accent}):Play()
end)
------------------------------------------------------------------------
-- OPEN/CLOSE HOTKEYS
------------------------------------------------------------------------
conn(UIS.InputBegan:Connect(function(inp, gpe)
if inp.KeyCode == Enum.KeyCode.Escape and chatOpen then
chatOpen = false
main.Visible = false
return
end
if gpe then return end
if inp.KeyCode == Enum.KeyCode.Slash then
chatOpen = not chatOpen
main.Visible = chatOpen
if chatOpen then
task.defer(function()
inputBox:CaptureFocus()
end)
end
end
end))
------------------------------------------------------------------------
-- MOBILE OPEN BUTTON
------------------------------------------------------------------------
local isMobile = UIS.TouchEnabled and not UIS.KeyboardEnabled
local openBtn = Instance.new("TextButton")
openBtn.Size = UDim2.new(0, 50, 0, 50)
openBtn.Position = isMobile and UDim2.new(1, -60, 0.4, 0) or UDim2.new(1, -60, 0, 10)
openBtn.BackgroundColor3 = C.accent
openBtn.BackgroundTransparency = 0.2
openBtn.BorderSizePixel = 0
openBtn.Text = "🤖💬"
openBtn.TextSize = 24
openBtn.Font = Enum.Font.GothamBold
openBtn.TextColor3 = C.white
openBtn.Active = true
openBtn.Draggable = true
openBtn.Parent = gui
Instance.new("UICorner", openBtn).CornerRadius = UDim.new(0, 25)
Instance.new("UIStroke", openBtn).Color = C.border
-- Notification dot
local notifDot = Instance.new("Frame")
notifDot.Size = UDim2.new(0, 14, 0, 14)
notifDot.Position = UDim2.new(1, -6, 0, -4)
notifDot.BackgroundColor3 = C.green
notifDot.BorderSizePixel = 0
notifDot.Parent = openBtn
Instance.new("UICorner", notifDot).CornerRadius = UDim.new(1, 0)
openBtn.MouseButton1Click:Connect(function()
chatOpen = not chatOpen
main.Visible = chatOpen
if chatOpen then
notifDot.Visible = false
if not isMobile then
task.defer(function() inputBox:CaptureFocus() end)
end
end
end)
-- Pulse animation on open button
task.spawn(function()
while task.wait(2) do
if not chatOpen then
TS:Create(openBtn, TweenInfo.new(0.5, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut), {
BackgroundTransparency = 0.5
}):Play()
task.wait(0.5)
TS:Create(openBtn, TweenInfo.new(0.5, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut), {
BackgroundTransparency = 0.2
}):Play()
end
end
end)
------------------------------------------------------------------------
-- ANTI-AFK
------------------------------------------------------------------------
do
local vu = game:GetService("VirtualUser")
conn(LP.Idled:Connect(function()
vu:CaptureController()
vu:ClickButton2(Vector2.new())
end))
end
------------------------------------------------------------------------
-- INIT
------------------------------------------------------------------------
showWelcome()
print("=== AI CHATBOT v1 ===")
print(" Press / to open chat")
print(" Press ESC to close")
print(" Or tap the 💬 button")
if httpRequest then
print(" HTTP detected! Add API keys in config for full AI.")
else
print(" No HTTP function found. Using smart fallback bot.")
end
print("=====================")