sb0t includes a built-in captcha system that presents joining users with a text-art word puzzle before they are allowed to participate in the room. This is a lightweight defence against automated bots and join-flooding scripts, requiring a human to read and type a short word before their first message is relayed to other room members.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/AresChat/sb0t/llms.txt
Use this file to discover all available pages before exploring further.
How the Captcha Works
Initialisation
When the server starts,Captcha.Initialize() loads the word list embedded in the captcha assembly (captcha/Resources/4words.txt) — a whitespace-separated list of common four-letter words. Only words that are exactly 4 characters long are retained:
Challenge Generation
When a user needs to be challenged,Captcha.Create() picks a random word from the list and renders it as a 5-row ASCII/text-art image. Each row is prefixed with the colour-code byte \x0005 so the Ares client renders it in a specific colour. The result is a CaptchaItem with:
Word— the correct answer stringLines— aString[5]array, each element being one row of the text-art rendering
NOSUCH packets (for web clients) or NoSuch TCP packets (for Ares desktop clients), surrounded by blank lines to visually separate it from chat:
Answer Verification
Once the challenge is displayed, the user’s very next public message is interpreted as their answer attempt. The comparison is case-insensitive and strips Ares colour codes before comparing:- Wrong answer — a new challenge is generated and sent; the
Events.CaptchaReplyevent fires with the incorrect text. - Correct answer —
IUser.Captchais set totrue, the user is unquarantined if applicable, the GUID is recorded in the persistent passed-captcha database, andCaptchaManager.AddCaptcha(client)is called.
Persistent Pass-Through (GUID Memory)
Once a user passes the captcha their client GUID is written to an SQLite database at:CaptchaManager.HasCaptcha(client) checks this database on every new connection. If the user’s GUID is found and the stored timestamp is within the last 14 days (1,209,600 seconds), the captcha is skipped automatically. The record’s timestamp is refreshed on each visit within that window (the 7-day rolling refresh), meaning regular visitors never see the captcha again. Records older than 14 days are purged at server startup.
Enabling the Captcha
Toggle captcha in the sb0t GUI. When captcha is enabled:Captcha = false. The captcha_mode setting controls what happens next:
captcha_mode value | Behaviour |
|---|---|
0 | The user joins normally but cannot send messages until they pass (non-quarantine mode) |
1 | The user is quarantined on join — they appear in the room but are invisible to other users until they pass |
Captcha = true, Quarantined = false immediately.
Quarantined Users
Whencaptcha_mode is 1, a user who has not yet passed arrives in a quarantined state (IUser.Quarantined = true). They:
- Receive the challenge image via
NOSUCHpackets - Are not announced to other room members (no
JOINbroadcast is sent) - Receive only a minimal user list (empty —
USERLIST_ENDis sent immediately) - Cannot send public messages, emotes, or private messages to other users
client.Unquarantine() is called, which triggers the normal join broadcast and populates the full user list for the newly-admitted user.
Scripting Integration
| Event | When it fires |
|---|---|
Events.CaptchaSending(client) | Just before each challenge image is sent to the user |
Events.CaptchaReply(client, text) | When the user sends a reply (correct or incorrect); the text parameter is the raw typed string |
Events.Joining(client) | Fires during the login sequence before the captcha check is applied; returning false in a script rejects the user before a captcha is ever sent |