Skip to main content

Chat Room Rules & Moderation

Spritz provides a comprehensive moderation system for managing chat rooms across all chat types — channels, groups, location chats, and Alpha Chat. Admins and moderators can configure content rules, enforce slow mode, ban disruptive users, and set custom room guidelines.

Overview

The moderation system has four layers:

┌─────────────────────────────────────────────────────────────────┐
│ Moderation Layers │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. Room Rules (content control) │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Role-based content toggles, slow mode, read-only, │ │
│ │ max message length, custom guidelines text │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ 2. Blocked Words (anti-scam / content filter) │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Global or per-room blocked words/phrases, regex, │ │
│ │ actions: block, flag, mute │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ 3. Moderator Permissions (role-based) │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Pin, delete, mute, manage mods — per moderator; │ │
│ │ shared moderators for official channels │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ 4. Room Bans (user-level enforcement) │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Temporary or permanent bans with reasons, │ │
│ │ removes user from room membership │ │
│ └──────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘

Supported Chat Types

Chat TypeRoom RulesModeratorsRoom Bans
Channels
Token Chats✅ (admin/mod roles)
Groups
Location Chats
Alpha Chat

For token chats, use chatType: "token" and chatId set to the token chat id (e.g. tc_xxxxxxxxxxxxxxxx). Only the chat creator or members with admin/moderator role can manage rules and bans.


Room Rules

Room rules control what types of content can be posted and set behavioral constraints for a chat room. Content permissions are role-based: you can allow everyone, restrict to moderators only, or disable a content type entirely.

Content Permission Levels

Each content type (links, photos, GIFs, etc.) uses one of three levels:

LevelValueDescription
EveryoneeveryoneAll members can use this content type
Mods onlymods_onlyOnly admins and moderators can use it
DisableddisabledNo one can use this content type
Legacy Support

The API accepts legacy boolean values: true is treated as everyone, false as disabled.

Available Settings

SettingTypeDefaultDescription
links_allowedeveryone | mods_only | disabledeveryoneURLs and links in messages
photos_allowedsameeveryonePhoto uploads
pixel_art_allowedsameeveryonePixel art messages
gifs_allowedsameeveryoneGIF images
polls_allowedsameeveryoneCreating polls
location_sharing_allowedsameeveryoneLocation sharing
voice_allowedsameeveryoneVoice messages
read_onlybooleanfalseOnly admins and moderators can post
slow_mode_secondsinteger0Minimum seconds between messages (0 = off)
max_message_lengthinteger0Maximum characters per message (0 = unlimited)
rules_textstringnullCustom room guidelines shown to members

Slow Mode Options

ValueDescription
0Off (no delay)
55 seconds between messages
1010 seconds
3030 seconds
601 minute
3005 minutes
60010 minutes

Read-Only Mode

When read_only is enabled, only admins and moderators can post messages. Regular members can read messages but cannot send them. This is useful for announcement channels or during maintenance periods.

Custom Room Guidelines

Set rules_text to display custom guidelines that members can view via a banner in the chat. Guidelines support plain text and are shown in a modal when members tap "View Room Rules."


Chat Rules API

Get Room Rules

GET /api/chat-rules?chatType=channel&chatId={channelId}
ParameterTypeRequiredDescription
chatTypeenumYeschannel, alpha, location, group, token
chatIdstringYesChat room ID (or null for Alpha Chat)

Response

{
"rules": {
"id": "uuid",
"chat_type": "channel",
"chat_id": "channel-uuid",
"links_allowed": true,
"photos_allowed": true,
"pixel_art_allowed": true,
"gifs_allowed": true,
"polls_allowed": true,
"location_sharing_allowed": true,
"voice_allowed": true,
"slow_mode_seconds": 0,
"read_only": false,
"max_message_length": 0,
"rules_text": null
}
}

Update Room Rules

POST /api/chat-rules

Requires admin, room owner, or moderator with can_manage_mods permission.

Request Body

{
"chatType": "channel",
"chatId": "channel-uuid",
"rules": {
"links_allowed": false,
"slow_mode_seconds": 30,
"read_only": false,
"max_message_length": 500,
"rules_text": "Be respectful. No spam or self-promotion."
}
}

Moderator System

Permission Hierarchy

Spritz uses a tiered permission system. Higher-privilege roles inherit all lower permissions:

┌────────────────────────────────────────────┐
│ Super Admin (all permissions, can mute │
│ admins) │
├────────────────────────────────────────────┤
│ Admin (all permissions, cannot mute │
│ other admins) │
├────────────────────────────────────────────┤
│ Channel Owner / Group Admin │
│ (all permissions for their room) │
├────────────────────────────────────────────┤
│ Moderator (granular per-permission) │
│ • can_pin │
│ • can_delete │
│ • can_mute │
│ • can_manage_mods │
├────────────────────────────────────────────┤
│ Regular Member (no moderation powers) │
└────────────────────────────────────────────┘

Moderator Permissions

PermissionDescription
can_pinPin and unpin messages
can_deleteDelete any message in the room
can_muteMute users and manage room bans
can_manage_modsPromote and demote other moderators

Moderation API

Get Permissions

GET /api/moderation?action=permissions&userAddress=0x...&channelId={id}

List Moderators

GET /api/moderation?action=moderators&channelId={id}

List Muted Users

GET /api/moderation?action=muted&channelId={id}

Check If User Is Muted

GET /api/moderation?action=check-muted&userAddress=0x...&channelId={id}

View Moderation Log

GET /api/moderation?action=mod-log&requestingUser=0x...&channelId={id}

Moderation Actions

All moderation actions are performed via POST /api/moderation:

Promote Moderator

{
"action": "promote-mod",
"moderatorAddress": "0x...",
"channelId": "channel-uuid",
"targetAddress": "0xnewmod...",
"canPin": true,
"canDelete": true,
"canMute": true,
"canManageMods": false,
"notes": "Trusted community member"
}

Demote Moderator

{
"action": "demote-mod",
"moderatorAddress": "0x...",
"channelId": "channel-uuid",
"targetAddress": "0xmod..."
}

Mute a User

{
"action": "mute-user",
"moderatorAddress": "0x...",
"channelId": "channel-uuid",
"targetAddress": "0xuser...",
"duration": "24h",
"reason": "Repeated spam"
}
DurationDescription
1h1 hour
24h24 hours
7d7 days
permanentNo expiration
warning

Admins cannot be muted by other admins. Only super admins can mute admins. Moderators cannot mute other moderators unless they have can_manage_mods permission.

Unmute a User

{
"action": "unmute-user",
"moderatorAddress": "0x...",
"channelId": "channel-uuid",
"targetAddress": "0xuser..."
}

Delete a Message

{
"action": "delete-message",
"moderatorAddress": "0x...",
"messageId": "message-uuid",
"messageType": "channel",
"reason": "Violates community guidelines"
}

Pin / Unpin a Message

{
"action": "pin-message",
"moderatorAddress": "0x...",
"messageId": "message-uuid",
"messageType": "channel",
"shouldPin": true
}

Room Bans

Room bans are more severe than mutes — they remove the user from the room entirely and prevent them from rejoining.

Ban API

List Active Bans

GET /api/chat-rules/ban?chatType=channel&chatId={id}

Check If User Is Banned

GET /api/chat-rules/ban?chatType=channel&chatId={id}&action=check&userAddress=0x...

Ban a User

POST /api/chat-rules/ban
{
"action": "ban",
"chatType": "channel",
"chatId": "channel-uuid",
"targetAddress": "0xuser...",
"reason": "Harassment",
"duration": "permanent"
}
DurationDescription
1h1 hour
24h24 hours
7d7 days
30d30 days
permanentNo expiration

When a user is banned:

  1. Their ban record is created in shout_room_bans
  2. They are removed from the room membership
  3. They cannot rejoin until the ban expires or is lifted
danger

You cannot ban admins or yourself. Only admins, room owners, and moderators with can_mute permission can ban users.

Unban a User

{
"action": "unban",
"chatType": "channel",
"chatId": "channel-uuid",
"targetAddress": "0xuser..."
}

Blocked Words (Anti-Scam)

The blocked words system lets admins and moderators block or flag specific words or phrases (including regex patterns) to reduce scam and abuse. Words can be global (platform-wide) or room-specific.

Scope and Actions

ScopeDescriptionWho Can Manage
globalApplies to all chatsAdmins and global moderators
roomApplies to one channel, group, location chat, or AlphaRoom owner, admins, or moderators with can_manage_mods
ActionDescription
blockMessage is rejected; user sees an error
flagMessage can be flagged for review (future use)
muteTreated as restricted (message rejected with generic message)

Blocked Words API

List Blocked Words

GET /api/blocked-words?scope=global
GET /api/blocked-words?scope=room&chatType=channel&chatId=xxx
GET /api/blocked-words?scope=all&chatType=channel&chatId=xxx
ParameterDescription
scopeglobal, room, or all (global + room combined)
chatTypeRequired for room/all: channel, alpha, location, group, token
chatIdRoom ID for room scope

Add a Blocked Word

POST /api/blocked-words
{
"word": "scam-phrase",
"scope": "global",
"chatType": null,
"chatId": null,
"action": "block",
"isRegex": false
}
  • word: The word or phrase (or regex pattern if isRegex: true). Stored lowercased for non-regex.
  • scope: global or room.
  • chatType / chatId: Required when scope is room.
  • action: block, flag, or mute.
  • isRegex: If true, word is treated as a case-insensitive regex pattern.

Remove a Blocked Word

DELETE /api/blocked-words
{ "id": "uuid-of-blocked-word" }

Soft delete: the word is set to is_active: false.

Shared Moderators for Official Channels

For official channels (is_official: true), global moderators (moderators with channel_id: null) can also manage that channel. They can manage blocked words, room rules, and moderation for any official channel without being added as a per-channel moderator.


Role Badges

Spritz shows role badges next to usernames in all chat types so members can see who is staff or a moderator:

BadgeRoleDescription
Spritz TeamSuper adminPlatform super admins
AdminAdminPlatform admins
ModModeratorGlobal or channel moderator

Badges are resolved via the useRoleBadges hook (admins and global/channel moderators). For a given channel, both global moderators and channel-specific moderators are considered.


Message Validation

When a user sends a message, the server validates it against the room's rules:

  1. Ban check — Is the user banned from this room?
  2. Read-only check — Is the room read-only? (admins/mods are exempt)
  3. Content type check — Is the message type allowed for this user's role? (everyone vs mods_only vs disabled)
  4. Blocked words check — For text messages, content is checked against global and room-specific blocked words (exact or regex). Violations return an error and the message is not sent.
  5. Link detection — If links are disabled for the user's role, scan text messages for URLs
  6. Length check — Does the message exceed max_message_length?
// Server-side validation flow
const rules = await getChatRules(chatType, chatId);

// 1. Check if user is banned
const isBanned = await isUserBanned(chatType, chatId, userAddress);
if (isBanned) {
return { allowed: false, reason: "You are banned from this room" };
}

// 2. Check read-only mode (admins/mods exempt)
if (rules.read_only && !isAdminOrMod) {
return { allowed: false, reason: "This room is read-only" };
}

// 3. Check content type
if (messageType === "image" && !rules.photos_allowed) {
return { allowed: false, reason: "Photos are not allowed" };
}

// 4. Check for links in text
if (!rules.links_allowed && containsUrl(content)) {
return { allowed: false, reason: "Links are not allowed" };
}

// 5. Check message length
if (rules.max_message_length > 0 && content.length > rules.max_message_length) {
return { allowed: false, reason: `Message exceeds ${rules.max_message_length} characters` };
}

Database Schema

Room Rules Table

CREATE TABLE shout_chat_rules (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
chat_type TEXT NOT NULL CHECK (chat_type IN ('channel', 'alpha', 'location', 'group')),
chat_id TEXT,
links_allowed BOOLEAN DEFAULT true,
photos_allowed BOOLEAN DEFAULT true,
pixel_art_allowed BOOLEAN DEFAULT true,
gifs_allowed BOOLEAN DEFAULT true,
polls_allowed BOOLEAN DEFAULT true,
location_sharing_allowed BOOLEAN DEFAULT true,
voice_allowed BOOLEAN DEFAULT true,
slow_mode_seconds INTEGER DEFAULT 0,
read_only BOOLEAN DEFAULT false,
max_message_length INTEGER DEFAULT 0,
rules_text TEXT,
updated_by TEXT,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE(chat_type, chat_id)
);

CREATE INDEX idx_chat_rules_lookup ON shout_chat_rules(chat_type, chat_id);

Room Bans Table

CREATE TABLE shout_room_bans (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
chat_type TEXT NOT NULL CHECK (chat_type IN ('channel', 'alpha', 'location', 'group')),
chat_id TEXT,
user_address TEXT NOT NULL,
banned_by TEXT NOT NULL,
reason TEXT,
banned_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
banned_until TIMESTAMP WITH TIME ZONE, -- NULL = permanent
is_active BOOLEAN DEFAULT true
);

CREATE UNIQUE INDEX idx_room_bans_unique
ON shout_room_bans(chat_type, COALESCE(chat_id, '__global__'), user_address)
WHERE is_active = true;
CREATE INDEX idx_room_bans_lookup
ON shout_room_bans(chat_type, chat_id, user_address, is_active);

Blocked Words Table

CREATE TABLE shout_blocked_words (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
word TEXT NOT NULL,
scope TEXT NOT NULL DEFAULT 'global' CHECK (scope IN ('global', 'room')),
chat_type TEXT CHECK (chat_type IN ('channel', 'alpha', 'location', 'group')),
chat_id TEXT,
action TEXT NOT NULL DEFAULT 'block' CHECK (action IN ('block', 'flag', 'mute')),
is_regex BOOLEAN DEFAULT false,
added_by TEXT NOT NULL,
added_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
is_active BOOLEAN DEFAULT true
);

CREATE UNIQUE INDEX idx_blocked_words_unique
ON shout_blocked_words(LOWER(word), scope, COALESCE(chat_type, '__none__'), COALESCE(chat_id, '__global__'))
WHERE is_active = true;
CREATE INDEX idx_blocked_words_global ON shout_blocked_words(scope, is_active) WHERE scope = 'global' AND is_active = true;
CREATE INDEX idx_blocked_words_room ON shout_blocked_words(chat_type, chat_id, is_active) WHERE scope = 'room' AND is_active = true;

Moderators Table

CREATE TABLE shout_moderators (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_address TEXT NOT NULL,
channel_id UUID REFERENCES shout_public_channels(id) ON DELETE CASCADE,
granted_by TEXT NOT NULL,
granted_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
can_pin BOOLEAN DEFAULT true,
can_delete BOOLEAN DEFAULT true,
can_mute BOOLEAN DEFAULT true,
can_manage_mods BOOLEAN DEFAULT false,
notes TEXT,
UNIQUE(user_address, channel_id)
);

Moderation Log Table

CREATE TABLE shout_moderation_log (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
action_type TEXT NOT NULL,
moderator_address TEXT NOT NULL,
target_user_address TEXT,
target_message_id UUID,
channel_id UUID,
reason TEXT,
metadata JSONB,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

UI Components

ChatRulesPanel

The ChatRulesPanel component provides a three-tab interface for managing room settings:

TabFeatures
ContentToggle content types (links, photos, GIFs, etc.), read-only mode, slow mode selector
RulesText editor for custom room guidelines with preview
BansList active bans, ban/unban users, view ban reasons and durations

ChatRulesBanner

A member-facing banner that appears when rules_text is set. Members can tap "View Room Rules" to see the guidelines in a modal dialog.

ModerationPanel

A two-tab interface for managing moderators and muted users:

TabFeatures
ModeratorsAdd/remove moderators with individual permission toggles
Muted UsersView muted users with durations, unmute users

Best Practices

  1. Start permissive — Only restrict content types if there's a specific problem
  2. Use slow mode for large rooms — 10-30 seconds prevents spam without frustrating users
  3. Write clear guidelines — Use rules_text to set expectations before issues arise
  4. Ban as last resort — Try muting first; bans remove users from the room entirely
  5. Audit moderator actions — Review the moderation log regularly via the admin panel
  6. Grant minimal permissions — Only give moderators the specific permissions they need

Next Steps