Reference

Documentation

Everything you need to know about silicord is all listed below.

Commands

Prefix Commands

Create a command that responds to a prefix like !. The message object and an args table are passed to your handler.

lua
-- Basic command
client:CreateCommand("ping", function(message, args)
    message:Reply("Pong!")
end)

-- With arguments: !say hello world
client:CreateCommand("say", function(message, args)
    -- args[1] = "hello", args[2] = "world"
    -- args.raw = "hello world"
    message:Reply(args.raw)
end)

-- task.wait inside commands
client:CreateCommand("countdown", function(message, args)
    message:Reply("3...")
    silicord.task.wait(1)
    message:Reply("2...")
    silicord.task.wait(1)
    message:Reply("1... Go!")
end)

Slash Commands

Requires app_id in your Connect config. Supported argument types: string, integer, number, bool, user, channel, role, any.

lua
client:CreateSlashCommand("ban", {
    description = "Ban a user from the server",
    options = {
        { name = "user",   description = "The user to ban",    type = "user",   required = true  },
        { name = "reason", description = "Reason for the ban", type = "string", required = false }
    }
}, function(interaction, args)
    interaction:Reply("Banned " .. args.user .. ". Reason: " .. (args.reason or "none"))
end)

Error Handling

Listen to client.OnError to catch command errors without crashing the bot. The signal fires with an error type string, the context object, the command name, and the error message.

lua
client.OnError:Connect(function(error_type, ctx, name, detail)
    if error_type == "CommandNotFound" then
        ctx:Reply("❌ Unknown command `!" .. name .. "`.")
    elseif error_type == "MissingArgument" then
        ctx:Reply("❌ Missing argument for `!" .. name .. "`.")
    elseif error_type == "CommandError" then
        ctx:Reply("❌ Something went wrong running `!" .. name .. "`.")
        print("CommandError in !" .. name .. ": " .. detail)
    elseif error_type == "SlashCommandError" then
        ctx:Reply("❌ Something went wrong running `/" .. name .. "`.")
    elseif error_type == "ComponentError" then
        print("ComponentError in " .. name .. ": " .. detail)
    elseif error_type == "UnknownInteraction" then
        ctx:Reply("❌ Unknown slash command.")
    end
end)
Error type
When it fires
CommandNotFound
User typed an unregistered prefix command
MissingArgument
Command errored and args were empty
CommandError
Command callback threw a runtime error
SlashCommandError
Slash command callback threw a runtime error
ComponentError
Component callback threw a runtime error
UnknownInteraction
Slash command triggered with no registered handler

Message Object

The message object is passed to every prefix command handler.

v1.2.0: message:Reply() and message:Send() now return a Message object, so you can chain edits, reactions, or deletes on the sent message.
Method
Description
message:Reply(text)
Reply pinging the author; returns Message
message:Reply(text, embed)
Reply with text and an embed; returns Message
message:Reply(text, embed, components)
Reply with text, embed, and buttons/menus; returns Message
message:Send(text)
Send without pinging the author; returns Message
message:Send(text, embed)
Send with an embed, no ping; returns Message
message:Send(text, embed, components)
Send with all components; returns Message
message:Edit(text)
Edit the bot's own message
message:React("👍")
Add a reaction
message:Unreact("👍") v1.2.0
Remove a reaction the bot placed
message:Delete()
Delete the message
message:Pin()
Pin the message
message:Unpin()
Unpin the message
message:GetGuild()
Returns a Guild object (cached)
message:GetMember()
Returns a Member object for the author
message:SendPrivateMessage(text)
DM the message author
message:SendPrivateMessage(text, embed)
DM with an embed

Chaining on returned Message v1.2.0

lua
-- Edit the reply after a delay
client:CreateCommand("temp", function(message, args)
    local response = message:Reply("Processing...")
    silicord.task.wait(3)
    response:Edit("Done! ✅")
end)

-- Auto-delete the bot response
client:CreateCommand("autodelete", function(message, args)
    local response = message:Send("This disappears in 5 seconds.")
    silicord.task.delay(5, function()
        response:Delete()
    end)
end)

message.author

lua
message.author.id        -- user ID
message.author.username  -- username
message.author.bot       -- true if the author is a bot

Interaction Object

Passed to slash command handlers and component callbacks.

Method / Property
Description
interaction:Reply(text)
Reply to the interaction
interaction:Reply(text, embed)
Reply with embed
interaction:Reply(text, embed, components)
Reply with components
interaction:Update(text)
Update the original message (buttons)
interaction:Update(text, embed, components)
Update with embed and components
interaction:GetGuild()
Returns a Guild object
interaction:GetMember()
Returns a Member object
interaction:SendPrivateMessage(text)
DM the user who triggered this
interaction:SendPrivateMessage(text, embed)
DM with an embed
interaction.args
Slash command arguments keyed by name
interaction.values
Selected values from a select menu
interaction.author
The user who triggered the interaction
interaction.custom_id
The custom_id of the component that fired
interaction.guild_id
ID of the guild where the interaction occurred
interaction.channel_id
ID of the channel where it occurred

Member Object

Get a member from a message, interaction, or guild.

lua
local member = message:GetMember()
local member = interaction:GetMember()
local member = guild:GetMember(user_id)
Method
Description
member:Kick(reason)
Kick the member
member:Ban(reason, delete_days)
Ban the member
member:Timeout(seconds, reason)
Apply a timeout. Accepts a raw number or Enum.PunishmentLength value
member:RemoveTimeout()
Remove an active timeout
member:GiveRole(role_id)
Give the member a role
member:RemoveRole(role_id)
Remove a role from the member
member:SetNickname(nick)
Set a nickname
member:ResetNickname()
Reset to username
member:SendDM(text)
DM the member
member:SendDM(text, embed)
DM with an embed
member:GetStatus() v1.2.0
Returns an Enum.UserStatusInGuild value: checks if banned, timed out, or present

Timeout & status with Enum v1.2.0

lua
-- Raw seconds still work
member:Timeout(300, "Spamming")

-- Enum.PunishmentLength values (v1.2.0+)
member:Timeout(silicord.Enum.PunishmentLength["5Minutes"], "Spamming")
member:Timeout(silicord.Enum.PunishmentLength["1Hour"])
member:Timeout(silicord.Enum.PunishmentLength.Permanent, "Repeated violations")

-- Check member status (v1.2.0+)
local status = member:GetStatus()
if status == silicord.Enum.UserStatusInGuild.Banned then
    ctx:Reply("That user is already banned.")
end

member properties

lua
member.id        -- user ID
member.username  -- username
member.nickname  -- server nickname (nil if not set)
member.roles     -- table of role IDs
member.user      -- raw Discord user object

Guild Object

Get a guild from any message or interaction.

lua
local guild = message:GetGuild()
-- guild.id, guild.name
Method
Description
guild:CreateChannel(name, kind, options)
Create a channel. kind accepts a string name or Enum.ChannelType value
guild:EditChannel(channel_id, options)
Edit a channel
guild:DeleteChannel(channel_id)
Delete a channel
guild:CreateRole(name, color, permissions)
Create a role. color accepts hex string, integer, or Color3 object
guild:EditRole(role_id, options)
Edit a role. options.color accepts hex, integer, or Color3
guild:DeleteRole(role_id)
Delete a role
guild:GetMembers(limit)
List of Member objects (max 1000)
guild:GetMember(user_id)
Single Member by ID
guild:GetRandomMember()
Random Member object (from first 100)
guild:GetChannels()
All channels
guild:GetRoles()
All roles
guild:KickMember(user_id, reason)
Kick by ID
guild:BanMember(user_id, reason)
Ban by ID
guild:CreateEvent(options)
Create scheduled event
guild:EditEvent(event_id, options)
Edit scheduled event
guild:DeleteEvent(event_id)
Delete scheduled event
guild:GetEvents()
All scheduled events
guild:Edit(options)
Edit the server itself

Channel types

lua
-- String names
guild:CreateChannel("general",  "text")
guild:CreateChannel("General",  "voice")
guild:CreateChannel("Info",     "category")
guild:CreateChannel("news",     "announcement")
guild:CreateChannel("Stage",    "stage")
guild:CreateChannel("help",     "forum")
guild:CreateChannel("media",    "media")

-- Enum.ChannelType values (v1.2.0+)
guild:CreateChannel("general", silicord.Enum.ChannelType.Text)
guild:CreateChannel("Music",   silicord.Enum.ChannelType.Voice)

-- With options
guild:CreateChannel("general", "text", {
    topic     = "General chat",
    parent_id = "category_channel_id",
    nsfw      = false,
    slowmode  = 5
})
guild:CreateChannel("Music", "voice", {
    bitrate    = 64000,
    user_limit = 10
})

Scheduled Events

lua
guild:CreateEvent({
    name        = "Game Night",
    description = "Monthly game night!",
    type        = "external",   -- "stage", "voice", or "external"
    location    = "Discord Stage", -- for "external" type
    start_time  = "2025-09-01T20:00:00Z",
    end_time    = "2025-09-01T23:00:00Z"
})

guild:CreateEvent({
    name       = "Community Call",
    type       = "stage",
    channel_id = "stage_channel_id",
    start_time = "2025-09-01T20:00:00Z",
    end_time   = "2025-09-01T21:00:00Z"
})

guild:EditEvent(event_id, { name = "Updated Name" })
guild:DeleteEvent(event_id)
local events = guild:GetEvents()

Embeds

OOP API v1.0.0+

The preferred way to build embeds in v1.0.0+. Use silicord.Instance.new("Embed") for a clean, chainable builder. Pass a Color3 object directly to .Color

lua
local embed = silicord.Instance.new("Embed")
embed.Title       = "My Embed"
embed.Description = "This is the description."
embed.Color       = silicord.Color3.fromRGB(88, 101, 242)
embed.Url         = "https://example.com"
embed.Timestamp   = os.date("!%Y-%m-%dT%H:%M:%SZ")
embed.Author      = "Author Name"
embed.AuthorIcon  = "https://example.com/icon.png"
embed.AuthorUrl   = "https://example.com"
embed.Footer      = "Footer text"
embed.FooterIcon  = "https://example.com/icon.png"
embed.Image       = "https://example.com/image.png"
embed.Thumbnail   = "https://example.com/thumb.png"
embed:AddField("Field 1", "Value 1", true)
embed:AddField("Field 2", "Value 2", false)

message:Reply("Here is info:", embed:Build())
Property / Method
Description
embed.Title
Embed title string
embed.Description
Body text
embed.Color
Hex string, integer, or Color3 object
embed.Url
URL for the title link
embed.Timestamp
ISO 8601 timestamp string
embed.Author / .AuthorIcon / .AuthorUrl
Author section
embed.Footer / .FooterIcon
Footer section
embed.Image / .Thumbnail
Image URLs
embed:AddField(name, value, inline)
Add a field; returns the embed for chaining
embed:Build()
Returns the final table to pass to :Reply() etc.

Table syntax deprecated

Deprecated since v1.0.0. silicord.Embed({ ... }) still works but prints a runtime warning. Migrate to silicord.Instance.new("Embed") shown above. This helper will be removed in a future major version.
lua
-- ⚠ DEPRECATED — use silicord.Instance.new("Embed") instead
local embed = silicord.Embed({
    title       = "My Embed",
    description = "This is the description.",
    color       = "#5865F2"
})
message:Reply(embed)

Color3

A Roblox-style color class. In v1.0.0+, Color3 objects can be passed directly to any .Color / color field.

lua
local c = silicord.Color3.fromRGB(88, 101, 242)
local c = silicord.Color3.fromHex("#5865F2")

-- Pass directly to any color field (v1.0.0+)
embed.Color = silicord.Color3.fromHex("#57F287")

-- Use in guild:CreateRole
guild:CreateRole("Moderator", silicord.Color3.fromRGB(255, 165, 0))

-- Get the raw integer (rarely needed)
local int = c:ToInt()
Method / Property
Description
Color3.fromRGB(r, g, b)
Construct from 0–255 RGB components
Color3.fromHex(hex)
Construct from a hex string ("#RRGGBB" or "RRGGBB")
color.r / .g / .b
Individual channel values (0–255)
color:ToInt()
Returns the packed 24-bit integer (still available, rarely needed)

Buttons & Select Menus

OOP API v1.0.0+

Use silicord.Instance.new() to build components and action rows with a chainable, object-oriented style.

lua
client:CreateCommand("vote", function(message, args)
    local yes = silicord.Instance.new("Button")
    yes.Label    = "Yes"
    yes.Style    = "success"
    yes.CustomId = "vote_yes"

    local no = silicord.Instance.new("Button")
    no.Label    = "No"
    no.Style    = "danger"
    no.CustomId = "vote_no"

    local row = silicord.Instance.new("ActionRow")
    row:Add(yes)
    row:Add(no)

    message:Reply("Cast your vote!", nil, { row:Build() })
end)

client:CreateComponent("vote_yes", function(interaction)
    interaction:Update("You voted Yes! ✅")
end)

Button properties

Property
Description
button.Label
Button text (default: "Button")
button.Style
"primary", "secondary", "success", "danger", "link"
button.CustomId
ID matched by client:CreateComponent()
button.Url
URL for "link"-style buttons
button.Emoji
Emoji string (e.g. "👍")
button.Disabled
true to disable the button (default: false)
button:Build()
Returns the raw component table

Select Menus v1.0.0+

lua
client:CreateCommand("color", function(message, args)
    local menu = silicord.Instance.new("SelectMenu")
    menu.CustomId    = "color_pick"
    menu.Placeholder = "Pick a color"
    menu:AddOption("Red",   "red",   "A warm color")
    menu:AddOption("Blue",  "blue",  "A cool color")
    menu:AddOption("Green", "green", "A natural color")

    local row = silicord.Instance.new("ActionRow")
    row:Add(menu)

    message:Reply("Choose a color:", nil, { row:Build() })
end)

client:CreateComponent("color_pick", function(interaction)
    interaction:Update("You picked: **" .. interaction.values[1] .. "**")
end)

SelectMenu properties

Property / Method
Description
menu.CustomId
ID matched by client:CreateComponent()
menu.Placeholder
Placeholder text (default: "Select an option...")
menu.MinValues
Minimum selections required (default: 1)
menu.MaxValues
Maximum selections allowed (default: 1)
menu:AddOption(label, value, desc, emoji, default)
Add an option; returns the menu for chaining
menu:Build()
Returns the raw component table

Legacy table syntax deprecated

Deprecated since v1.0.0. The following helpers still work but emit a yellow runtime warning and will be removed in a future major version. Migrate to the OOP API above.
  • silicord.Embed({ ... })silicord.Instance.new("Embed")
  • silicord.Button({ ... })silicord.Instance.new("Button")
  • silicord.SelectMenu({ ... })silicord.Instance.new("SelectMenu")
  • silicord.ActionRow(...)silicord.Instance.new("ActionRow")
  • silicord.DataStore("name")silicord:GetService("DataStoreService"):GetDataStore("name")
lua
-- ⚠ DEPRECATED
local row = silicord.ActionRow(
    silicord.Button({ label = "Yes", style = "success", custom_id = "vote_yes" }),
    silicord.Button({ label = "No",  style = "danger",  custom_id = "vote_no"  })
)

GetService v1.2.0

silicord uses a service system inspired by Roblox's game:GetService(). Services are lazy-loaded so they are only created the first time you call GetService, saving memory.

lua
local DS       = silicord:GetService("DataStoreService")
local Http     = silicord:GetService("HttpService")
local Presence = silicord:GetService("PresenceService")
local AI       = silicord:GetService("AIService")
Service name
Description
"DataStoreService"
JSON file-based key-value persistence
"HttpService"
Make outbound HTTP/HTTPS requests
"PresenceService"
Set the bot's presence, status text, and activity
"AIService"
Connect an external OpenAI-compatible AI API

Enum v1.2.0

Roblox-style enums for type-safe values throughout your bot.

Enum.Presence

lua
silicord.Enum.Presence.Online        -- "online"
silicord.Enum.Presence.Idle          -- "idle"
silicord.Enum.Presence.DoNotDisturb  -- "dnd"
silicord.Enum.Presence.Invisible     -- "invisible"

Enum.ChannelType

lua
silicord.Enum.ChannelType.Text         -- 0
silicord.Enum.ChannelType.DM           -- 1
silicord.Enum.ChannelType.Voice        -- 2
silicord.Enum.ChannelType.GDM          -- 3
silicord.Enum.ChannelType.Category     -- 4
silicord.Enum.ChannelType.Announcement -- 5
silicord.Enum.ChannelType.Thread       -- 11
silicord.Enum.ChannelType.Stage        -- 13
silicord.Enum.ChannelType.Forum        -- 15
silicord.Enum.ChannelType.Media        -- 16

-- Pass directly to guild:CreateChannel()
guild:CreateChannel("general", silicord.Enum.ChannelType.Text)
guild:CreateChannel("Music",   silicord.Enum.ChannelType.Voice)

Enum.Permissions

Permission bit values for role creation and checks. 26 permissions total.

lua
silicord.Enum.Permissions.CreateInstantInvite  -- 0x1
silicord.Enum.Permissions.KickMembers          -- 0x2
silicord.Enum.Permissions.BanMembers           -- 0x4
silicord.Enum.Permissions.Administrator        -- 0x8
silicord.Enum.Permissions.ManageChannels       -- 0x10
silicord.Enum.Permissions.ManageGuild          -- 0x20
silicord.Enum.Permissions.AddReactions         -- 0x40
silicord.Enum.Permissions.ViewAuditLog         -- 0x80
silicord.Enum.Permissions.PrioritySpeaker      -- 0x100
silicord.Enum.Permissions.Stream               -- 0x200
silicord.Enum.Permissions.ReadMessages         -- 0x400
silicord.Enum.Permissions.ViewChannel          -- 0x400
silicord.Enum.Permissions.SendMessages         -- 0x800
silicord.Enum.Permissions.SendTTSMessages      -- 0x1000
silicord.Enum.Permissions.ManageMessages       -- 0x2000
silicord.Enum.Permissions.EmbedLinks           -- 0x4000
silicord.Enum.Permissions.AttachFiles          -- 0x8000
silicord.Enum.Permissions.ReadMessageHistory   -- 0x10000
silicord.Enum.Permissions.MentionEveryone      -- 0x20000
silicord.Enum.Permissions.UseExternalEmojis    -- 0x40000
silicord.Enum.Permissions.ViewGuildInsights    -- 0x80000
silicord.Enum.Permissions.Connect              -- 0x100000
silicord.Enum.Permissions.Speak                -- 0x200000
silicord.Enum.Permissions.MuteMembers          -- 0x400000
silicord.Enum.Permissions.DeafenMembers        -- 0x800000
silicord.Enum.Permissions.MoveMembers          -- 0x1000000
silicord.Enum.Permissions.UseVAD               -- 0x2000000
silicord.Enum.Permissions.ChangeNickname       -- 0x4000000
silicord.Enum.Permissions.ManageNicknames      -- 0x8000000
silicord.Enum.Permissions.ManageRoles          -- 0x10000000
silicord.Enum.Permissions.ManageWebhooks       -- 0x20000000
silicord.Enum.Permissions.ManageExpressions    -- 0x40000000
-- Create a role with a specific permission guild:CreateRole("Admin", silicord.Color3.fromRGB(255, 0, 0), silicord.Enum.Permissions.Administrator)

Enum.PunishmentLength

Pre-defined timeout durations in seconds. Pass directly to member:Timeout().

lua
silicord.Enum.PunishmentLength["1Minute"]   -- 60
silicord.Enum.PunishmentLength["5Minutes"]  -- 300
silicord.Enum.PunishmentLength["10Minutes"] -- 600
silicord.Enum.PunishmentLength["30Minutes"] -- 1800
silicord.Enum.PunishmentLength["1Hour"]     -- 3600
silicord.Enum.PunishmentLength["6Hours"]    -- 21600
silicord.Enum.PunishmentLength["12Hours"]   -- 43200
silicord.Enum.PunishmentLength["1Day"]      -- 86400
silicord.Enum.PunishmentLength["3Days"]     -- 259200
silicord.Enum.PunishmentLength["1Week"]     -- 604800
silicord.Enum.PunishmentLength.Permanent    -- nil (no end date)

member:Timeout(silicord.Enum.PunishmentLength["30Minutes"], "Spamming")
member:Timeout(silicord.Enum.PunishmentLength.Permanent,    "Repeated violations")

Enum.UserStatusInGuild

Returned by member:GetStatus().

lua
silicord.Enum.UserStatusInGuild.None     -- "none"
silicord.Enum.UserStatusInGuild.TimedOut -- "timed_out"
silicord.Enum.UserStatusInGuild.Kicked   -- "kicked"
silicord.Enum.UserStatusInGuild.Banned   -- "banned"

DataStoreService v1.2.0

JSON file-based key-value persistence. Each store saves to a <name>.datastore.json file. Calling GetDataStore("name") twice returns the same cached instance.

lua
local DS = silicord:GetService("DataStoreService")
local db = DS:GetDataStore("PlayerData")

db:SetAsync("score_user123", 500)

local score = db:GetAsync("score_user123")

local new_score = db:IncrementAsync("score_user123", 10)

db:RemoveAsync("score_user123")

local keys = db:GetKeys()
for _, k in ipairs(keys) do
    print(k, db:GetAsync(k))
end
Safety features: Empty or missing files start as a fresh store instead of crashing. Corrupt JSON is automatically backed up to name.datastore.json.corrupted_<timestamp> and the store resets. All writes use an atomic temp-file swap — a crash mid-write cannot corrupt your data. IncrementAsync resets non-numeric values to 0 with a warning instead of throwing.
Method
Description
store:SetAsync(key, value)
Write any JSON-serializable value
store:GetAsync(key)
Read a value; returns nil if missing
store:RemoveAsync(key)
Delete a key
store:IncrementAsync(key, delta)
Add delta to a numeric key (default +1)
store:GetKeys()
Returns a table of all keys in the store

HttpService v1.2.0

Make outbound HTTP/HTTPS requests from your bot.

lua
local Http = silicord:GetService("HttpService")

-- GET request
local body, code = Http:Get("https://api.example.com/data")

-- POST request
local body, code = Http:Post("https://api.example.com/submit", '{"key":"value"}')

-- Full control
local body, code = Http:Request("https://api.example.com", "PATCH", '{"x":1}', {
    ["X-Custom-Header"] = "abc"
})
Method
Description
Http:Get(url, headers?)
GET request; returns body, status_code
Http:Post(url, body, headers?)
POST request; returns body, status_code
Http:Request(url, method, body?, headers?)
Full request with any HTTP method

PresenceService v1.2.0

Set the bot's presence and activity text. Requires app_id in your Connect config.

lua
local Presence = silicord:GetService("PresenceService")

Presence:SetPresence(client, {
    presence = silicord.Enum.Presence.Online,  -- Enum.Presence value
    status   = "Watching over the server!",   -- text shown under the bot name
    type     = 3                               -- 0=Playing, 1=Streaming, 2=Listening, 3=Watching, 5=Competing
})
Option
Description
presence
Enum.Presence value — status indicator (default: Online)
status
Activity text displayed under the bot's name
type
Activity type: 0=Playing, 1=Streaming, 2=Listening, 3=Watching, 5=Competing (default: 0)

AIService v1.2.0 [BETA]

Connect an OpenAI-compatible AI to your bot. Works with OpenAI, Groq, and any provider using the /v1/chat/completions endpoint. THIS IS HEAVILY IN BETA TESTING.

lua
local AI = silicord:GetService("AIService")

AI:Configure("sk-your-api-key-here", {
    model    = "gpt-4o",                   -- default: "gpt-3.5-turbo"
    base_url = "https://api.openai.com/v1"  -- change for other providers
})

client:CreateSlashCommand("ask", {
    description = "Ask the AI a question",
    options = {
        { name = "question", description = "Your question", type = "string", required = true }
    }
}, function(interaction, args)
    local reply = AI:Prompt(args.question, "You are a helpful Discord bot assistant.")
    interaction:Reply(reply or "Sorry, I could not get a response.")
end)
Method
Description
AI:Configure(api_key, options?)
Set your API key; optionally override model and base_url
AI:Prompt(prompt, system?)
Send a prompt; returns the response string (or nil, error on failure)

DataStore deprecated

JSON file-based persistence. Each store maps to a <name>.datastore.json file on disk. Stores are cached in memory after the first load — calling silicord.DataStore("name") twice returns the same instance.

Deprecated in v1.2.0. silicord.DataStore("name") still works but prints a runtime warning. Migrate to silicord:GetService("DataStoreService"):GetDataStore("name") — the DataStore API methods are identical. See DataStoreService for full documentation.
lua
-- ⚠ DEPRECATED — use DataStoreService instead
local db = silicord.DataStore("PlayerData")

db:SetAsync("score_user123", 500)
local score = db:GetAsync("score_user123")
local new_score = db:IncrementAsync("score_user123", 10)
db:RemoveAsync("score_user123")

local keys = db:GetKeys()
for _, k in ipairs(keys) do
    print(k, db:GetAsync(k))
end
Method
Description
store:SetAsync(key, value)
Write any JSON-serializable value
store:GetAsync(key)
Read a value; returns nil if missing
store:RemoveAsync(key)
Delete a key
store:IncrementAsync(key, delta)
Add delta to a numeric key (default +1)
store:GetKeys()
Returns a table of all keys in the store

Signal

A Roblox-style event system. Create custom signals to decouple different parts of your bot. client.OnMessage and client.OnError are built-in signals exposed by the client.

lua
-- Create a custom signal
local mySignal = silicord.Signal.new()

-- Connect a listener (returns a connection)
local conn = mySignal:Connect(function(a, b)
    print("Signal fired!", a, b)
end)

-- Fire the signal
mySignal:Fire("hello", 42)

-- Disconnect later
conn.Disconnect()

-- Yield until the signal fires (must be inside a coroutine)
silicord.task.spawn(function()
    local a, b = mySignal:Wait()
    print("Resumed with:", a, b)
end)

-- Built-in: listen to all non-command, non-bot messages
client.OnMessage:Connect(function(message)
    print(message.author.username .. ": " .. message.content)
end)
Method
Description
Signal.new()
Create a new signal instance
signal:Connect(callback)
Add a listener; returns a connection object with .Disconnect()
signal:Fire(...)
Fire the signal; all listeners run in spawned threads
signal:Wait()
Yield the current coroutine until the signal fires; returns the fired arguments

Standard Libraries

silicord ships small utility modules that mirror Roblox's standard library extensions. They don't replace Lua's built-ins — they add helpers that Lua is missing.

silicord.math

Function
Description
silicord.math.clamp(n, min, max)
Clamp n between min and max
silicord.math.round(n)
Round to the nearest integer
silicord.math.lerp(a, b, t)
Linear interpolation; t in 0–1
silicord.math.sign(n)
Returns 1, -1, or 0

silicord.table

Function
Description
silicord.table.find(t, value)
Returns the index of value, or nil
silicord.table.contains(t, value)
Returns true if value is in the table
silicord.table.keys(t)
Returns all keys as a sequential table
silicord.table.values(t)
Returns all values as a sequential table
silicord.table.copy(t)
Shallow copy of a table

silicord.string

Function
Description
silicord.string.split(str, sep)
Split a string by a separator pattern (default: whitespace)
silicord.string.trim(str)
Remove leading and trailing whitespace
silicord.string.startsWith(str, prefix)
Returns true if str starts with prefix
silicord.string.endsWith(str, suffix)
Returns true if str ends with suffix
silicord.string.pad(str, length, char)
Right-pad str to length with char (default: space)
lua
local clamped = silicord.math.clamp(150, 0, 100)   -- 100
local lerped  = silicord.math.lerp(0, 10, 0.5)     -- 5.0

local parts = silicord.string.split("a,b,c", ",")  -- {"a","b","c"}
local clean = silicord.string.trim("  hello  ")    -- "hello"

local idx  = silicord.table.find({"a","b","c"}, "b") -- 2
local copy = silicord.table.copy(someTable)

CollectionService

A Roblox-style tag registry. Attach string tags to any Lua value (table, string, number, etc.) and query them later. Useful for flagging users, channels, or custom objects without storing extra state.

lua
local cs = silicord.CollectionService

cs:AddTag(message.author.id, "admin")
cs:AddTag(message.author.id, "vip")

print(cs:HasTag(message.author.id, "admin"))  -- true

local admins = cs:GetTagged("admin")
local tags   = cs:GetTags(message.author.id)  -- { "admin", "vip" }

cs:RemoveTag(message.author.id, "vip")
Method
Description
cs:AddTag(object, tag)
Attach a tag string to any value
cs:RemoveTag(object, tag)
Remove a specific tag
cs:HasTag(object, tag)
Returns true if the object has the tag
cs:GetTagged(tag)
Returns all objects with this tag
cs:GetTags(object)
Returns all tags on this object

Middleware

Middleware hooks run before every prefix command and slash command. Return false to block the command. Multiple middleware functions can be registered and run in order.

lua
-- Cooldown (3 seconds per user per command)
local cooldowns = {}
client:AddMiddleware(function(ctx, cmd, args)
    local key = ctx.author.id .. ":" .. cmd
    if os.time() - (cooldowns[key] or 0) < 3 then
        ctx:Reply("Slow down! Wait 3 seconds between commands.")
        return false
    end
    cooldowns[key] = os.time()
end)

-- Admin-only guard using CollectionService tags
silicord.CollectionService:AddTag(nil, "ban",  "AdminOnly")
silicord.CollectionService:AddTag(nil, "kick", "AdminOnly")

local ADMIN_ID = "123456789012345678"
client:AddMiddleware(function(ctx, cmd, args)
    if silicord.CollectionService:HasTag(nil, cmd, "AdminOnly") then
        if ctx.author.id ~= ADMIN_ID then
            ctx:Reply("You do not have permission to use this command.")
            return false
        end
    end
end)

Caching

silicord automatically caches guild and user data from Discord gateway events. message:GetGuild() checks the cache before making an HTTP request, avoiding unnecessary API calls.

lua
client.cache.guilds   -- raw guild data keyed by guild ID
client.cache.users    -- raw user data keyed by user ID
client.cache.bot_user -- the bot's own user object (set after READY)

Sharding

Sharding is fully automatic. silicord fetches the recommended shard count from Discord on startup and spawns the correct number of gateway connections with the required 5-second delay between each. You don't need to configure anything.

task scheduler

Roblox-style async helpers available anywhere in your bot code. All functions run inside copas coroutines — they never block the gateway loop.

Function
Description
silicord.task.wait(n)
Yield for n seconds; returns n
silicord.task.spawn(f, ...)
Run f in a new coroutine immediately
silicord.task.defer(f, ...) v1.0.0+
Run f on the next scheduler cycle — useful for avoiding stack overflows in recursive patterns
silicord.task.delay(n, f, ...) v1.0.0+
Call f(...) after n seconds without blocking the caller — the clean way to write timers
lua
-- Pause inside any coroutine
silicord.task.wait(2)

-- Fire and forget a background thread
silicord.task.spawn(function()
    silicord.task.wait(5)
    print("5 seconds later")
end)

-- defer: runs after the current frame completes
silicord.task.defer(function()
    print("deferred to next cycle")
end)

-- delay: clean one-shot timer
silicord.task.delay(10, function()
    print("10 seconds have passed!")
end)

-- Practical: send a temp message that auto-deletes
client:CreateCommand("temp", function(message, args)
    local response = message:Reply("This disappears in 5 seconds!")
    silicord.task.delay(5, function()
        response:Delete()
    end)
end)

Changelog

v1.2.0
GetService system: silicord:GetService() with lazy-loaded DataStoreService, HttpService, PresenceService, and AIService.
Enum: Enum.Presence, Enum.ChannelType, Enum.Permissions, Enum.PunishmentLength, Enum.UserStatusInGuild.
Message chaining: message:Reply() and message:Send() now return a Message object.
New methods: message:Unreact(), member:GetStatus().
Enum integration: member:Timeout() accepts Enum.PunishmentLength (including Permanent); guild:CreateChannel() accepts Enum.ChannelType.
Deprecated: silicord.DataStore() — use DataStoreService:GetDataStore() instead.
v1.1.0
18 bug fixes: OnMessage now fires correctly for plain messages; nil author guard for webhook/system messages; Signal fire snapshot safety; Signal:Wait coroutine guard; WebSocket large-frame header fix; GET request body fix; rate-limit retry cap; embed detection overhaul; ActionRow empty component guard; legacy ActionRow flatten fix; Lua 5.1 unpack compat shim; DataStore variable shadowing fix; Guild:CreateRole color fix; Message:Edit nil id guard; Run error type safety.
v1.0.0
DataStore safety: IncrementAsync now handles corrupt/empty/locked JSON without crashing; writes use atomic temp-file swapping; corrupt files are auto-backed-up.
Color3 UX: Color3 objects can be passed directly to all color fields — no more :ToInt().
task.defer(): Defers a function to the next scheduler cycle to avoid stack overflows.
task.delay(): Clean one-shot timer — call a function after n seconds without a manual loop.
Instance.new("SelectMenu") and Instance.new("ActionRow"): OOP classes added to match Embed and Button.
Deprecation warnings: silicord.Embed(), silicord.Button(), silicord.SelectMenu(), and silicord.ActionRow() now print runtime warnings. Migrate to silicord.Instance.new().
v0.4.3
Added a favicon
v0.4.2
Fixed typo in README.md
v0.4.1
Added new website: https://silicord.github.io/
v0.4.0
Added custom error handling via client.OnError
v0.3.3
Minor bug fixes and improvements
v0.3.2
Introduced token validation before startup
v0.3.1
Improved error messages for easier debugging
v0.3.0
Member object with full moderation actions; expanded channel types; channel/role editing and deletion; scheduled events; message:Send(), message:Edit(), message:Pin(), message:Unpin(); message:GetMember() and interaction:GetMember(); guild:Edit().
v0.2.2
Automatic sharding, buttons & select menus, rate limit bucket controller with auto-retry, state caching (client.cache), middleware system (client:AddMiddleware).
v0.2.0
Guild object, reactions (message:React()), embeds, DMs, prefix command arguments, slash commands, task.wait().
v0.1.0
silicord prototype — basic :Reply(), WebSocket gateway connection.
v0.0.2
Fixed WebSocket frame masking.