[Open Sourced] Custom Bubble Chat

Do you have to have a game where this error occurs so I can further investigate this possible bug?

Strangely enough, the issue stopped occurring and the error is no longer being thrown. Must’ve been something on my end. I’ll keep testing it and revert back to this thread if it occurs again.

Here’s a place with all the components that are used for the chat with the channels:
https://www.roblox.com/games/4754926380/Chat-Channels

1 Like


Might wanna do some further bug testing.
image

1 Like

Wow. I did not even realize it sent a message twice. That’s probably the reason. Working on a fix right now. Shouldn’t take that long.

1 Like

So uh, silly mistake. When I made the Model version of this Bubble Chat, I had it in Workspace. The script was still in there and it wasn’t disabled. So two scripts were running at the same time. I’ve disabled the scripts and now this should solve some problems. If you find another bug, please reply.

2 Likes

Hey, would it be possible to hide messages that start with ‘’/e’’ and to make emojis appear upon using things such as ‘’:flushed :". Also, my AFK timer doesn’t seem to work. For the rest it’s great! :partying_face:

3 Likes

Thank you so much for creating this, very helpful.

1 Like

Thanks! this will be very useful.

2 Likes

Where can we find the script for that?

image So if you type and stop typing this shows up and the loading bubbles keep on showing even though you stopped typing.

5 Likes

Amazing, I like it. Thank you for open-sourcing this!

1 Like

Amazing, I might actually use it in my games. Thanks.

1 Like

Also private chats show up with the bubble chat.

1 Like

I haven’t had time to update this Chat and I really apologize for that. Sorry for all the bugs occurring. From now, I would recommend using this as a learning resource instead of a game asset(especially since you can see private messages) and I don’t think I will have time to fix these bugs. If I do manage to find time to update it, I will let you all know. Thanks.

7 Likes

Very useful, its been helping me understand the chat system a lot. Looks good but there is a lot of bugs ^^^. If the bugs get fixed there is a lot of people that would pay some robux for it. :slight_smile:

1 Like

I rather let people learn from it for free instead of myself having to sell it as an asset.

1 Like

Can you help me out on making this custom bubble chat able to be used for only Bubble Chat? So it doesn’t show up the classic chat (I tried enabling bubble chat only and it shows both ROBLOX’s and your bubble).

1 Like

I don’t know if the creator will respond but on mobile it says your typing for forever. When walking around if still has the typing icon and the afk timer takes down your keyboard when typing on mobile every 5 seconds, requiring u to type your message in less than 5 seconds.

Oh man really wish u fixed the bug when u r not typing it keeps going

did you do everything correct?

ReplicatedStorage → Modules → Chat

Code:

ModuleScript

local Chat = {
Players = {}
}

– // Services \ –
local ReplicatedStorage = game:GetService(“ReplicatedStorage”)
local ServerStorage = game:GetService(“ServerStorage”)
local TweenService = game:GetService(“TweenService”)
local TextService = game:GetService(“TextService”)

– // Main \ –
function UpdateMessageNames(Player)
local Character = Player.Character
local BubbleChatUI = Character.Head.BubbleChatUI

for _, MessageLabel in pairs(BubbleChatUI:GetChildren()) do
	if MessageLabel.Name ~= "TypingLabel" then
		MessageLabel.Name = tostring(tonumber(MessageLabel.Name) + 1)
	end
end

end

function Chat:GetMessageIndex(Player, Time)
local Messages = Chat.Players[Player.Name].Messages
for i,Message in pairs(Messages) do
if Message.Time == Time then
return i
end
end

return nil

end

function Chat:DeleteMessage(Player, messageLabel) – // Deletes message from the given index.
local Character = Player.Character
if Character == nil then return end
local Messages = Chat.Players[Player.Name].Messages
local MessageIndex = nil

if typeof(messageLabel) == "number" then -- // Checks if the paramater is an messageLabel(instance) or the index(number).
	MessageIndex = messageLabel
	messageLabel = Messages[MessageIndex].Instance
else
	if Character.Head.BubbleChatUI:FindFirstChild(messageLabel.Name) == nil then
		return 
	end
	if messageLabel:FindFirstChild("Time") == nil then
		return 
	end
	
	MessageIndex = Chat:GetMessageIndex(Player, messageLabel.Time.Value)
end 

print("Deleting: "..MessageIndex)

local FadeOutMessage = TweenService:Create(messageLabel, TweenInfo.new(0.5), {
	ImageTransparency = 1,
})
local FadeOutText = TweenService:Create(messageLabel.TextBox, TweenInfo.new(0.5), {
	TextTransparency = 1,
})

table.remove(Messages, MessageIndex)

if #Messages == 0 then -- // If message == 0 then we will delete the player from Chat.Players.
	Chat.Players[Player.Name] = nil
end 

FadeOutMessage:Play()
FadeOutText:Play()
FadeOutMessage.Completed:Wait()
messageLabel:Destroy()

end

function Chat:FilterContent(Player, str) – // Filter message.
local Success, FilteredStr = pcall(TextService.FilterStringAsync, TextService, str, Player.UserId, Enum.TextFilterContext.PublicChat)

if Success then
	return FilteredStr:GetChatForUserAsync(Player.UserId)
else
	return ""
end

end

function Chat:PushMessages(Player, direction)
local Character = Player.Character
local BubbleChatUI = Character.Head.BubbleChatUI

for i = 1, 4 do
	local MessageLabel = BubbleChatUI:FindFirstChild(tostring(i))
	if MessageLabel then
		local LastMessage = BubbleChatUI:FindFirstChild(tostring(i-1))
		local MessageYSize =  math.floor(MessageLabel.Size.Y.Scale * 100) / 100
		local MessageYChange = (MessageYSize == 0.2 and 0.75 or 0.85)
		local LMessageYSize 
		local MessageYChange2
		
		if LastMessage then
			LMessageYSize = math.floor(LastMessage.Size.Y.Scale * 100) / 100
			MessageYChange2 = LMessageYSize == 0.1 and 0.1 or 0.2
		end
		
		MessageLabel.YPos.Value = LastMessage ~= nil 
		and (LastMessage.YPos.Value - 0.1 - (MessageYSize == 0.1 and 0 or 0.1)) --and (MessageYSize == 0.2 and 0.1 or 0) or 0))
		or (direction == 1 and (MessageYChange - 0.1)
	    or (MessageYChange))
		
	    TweenService:Create(MessageLabel, TweenInfo.new(0.3), {
	        Position = UDim2.new(
	            MessageLabel.Position.X.Scale, 
	            0,
	            MessageLabel.YPos.Value,
	            0
	        ) 
	    }):Play()
	end
end

end

function Chat:Typing(Player, IsTyping)
local Messages = nil
local Character = Player.Character
local BubbleChatUI = Character.Head.BubbleChatUI
if Chat.Players[Player.Name] ~= nil then Messages = Chat.Players[Player.Name].Messages end

if IsTyping and BubbleChatUI:FindFirstChild("TypingLabel") == nil then
    local TypingLabel = ReplicatedStorage.Assets.BubbleChat.TypingLabel:Clone()
    TypingLabel.Position = UDim2.new(0.15, 0, 1.1, 0)
	TypingLabel.Parent = BubbleChatUI
	
    Chat:PushMessages(Player, 1)

    TweenService:Create(TypingLabel, TweenInfo.new(0.3), {
        Position = UDim2.new(0.15, 0, 0.85, 0)
    }):Play()

    repeat 
        wait(0.05)
		for i = 1, 3 do
			local Dot = TypingLabel:FindFirstChild("Dot"..i)
			if TypingLabel:FindFirstChild("Dot"..i) == nil then break end
			
			local DotTween = TweenService:Create(Dot.UIScale, TweenInfo.new(0.3, Enum.EasingStyle.Sine, Enum.EasingDirection.In, 0, true, 0), {
       			Scale = 2
			})
			DotTween:Play()
			DotTween.Completed:Wait()
		end
    until TypingLabel == nil
else
	if BubbleChatUI:FindFirstChild("TypingLabel") == nil then return end
	
	local TweenOut = TweenService:Create(BubbleChatUI.TypingLabel, TweenInfo.new(0.3), {
		Position = UDim2.new(0.15, 0, 1.1, 0)
	})
		
	if #Messages > 0 and (tick()-Messages[#Messages].Time) >= 1.5 then 
		Chat:PushMessages(Player, 0) 
	elseif #Messages == 0 then
		TweenOut:Play()
		TweenOut.Completed:Wait()
	end
	
	BubbleChatUI.TypingLabel:Destroy()
end

end

function Chat:CreateMessageLabel(bubblechatUI, content)
local LabelHeight = #content >= 30 and 0.2 or 0.1

local MessageLabel = ReplicatedStorage.Assets.BubbleChat.MessageLabel:Clone()
MessageLabel.TextBox.Text = content
MessageLabel.Size = UDim2.new(0.7, 0, LabelHeight, 0)
MessageLabel.Position = UDim2.new(0.15, 0, LabelHeight == 0.1 and 0.85 or 0.75, 0)
MessageLabel.Parent = bubblechatUI

return MessageLabel

end

function Chat:SendMessage(Player, str)
local Character = Player.Character
local FilteredContent = Chat:FilterContent(Player, str)

if Character == nil then return end -- // Doesn't run if character doesn't exist.

if not Chat.Players[Player.Name] then
    Chat.Players[Player.Name] = {
		Spamming = nil,
		Messages = {}
	}
end

local PlayerStuff = Chat.Players[Player.Name]

local Messages = PlayerStuff.Messages
UpdateMessageNames(Player)
local MessageLabel = Chat:CreateMessageLabel(Character.Head.BubbleChatUI, FilteredContent)

local Message = {}
Message.Content = FilteredContent
Message.Time = tick()
Message.Instance = MessageLabel
MessageLabel.Name = 1
MessageLabel.Time.Value = Message.Time
MessageLabel.YPos.Value = MessageLabel.Position.Y.Scale

Chat:PushMessages(Player, 0)

table.insert(Messages, Message)

if #Messages >= 4 then
	Chat:DeleteMessage(Player, 1)
	print("Too many messages--deleting messsage 1.")
end

wait(10)
Chat:DeleteMessage(Player, MessageLabel)
print("Done deleting message " .. MessageLabel.Name)

end

return Chat

maybe that’s the problem