Per sneak's instruction: Use proper database types (boolean/integer columns), NOT a text string for modes. Each flag gets its own column.
Channel creator auto-gets +o: When a user JOINs and creates a new channel (ephemeral creation), they automatically get is_operator = 1.
2. MODE +o/+v/-o/-v (User Channel Modes)
MODE #channel +o nick — grant operator status (only existing +o can do this)
MODE #channel -o nick — revoke operator status
MODE #channel +v nick — grant voice (only +o can do this)
MODE #channel -v nick — revoke voice
NAMES reply must show @nick for operators, +nick for voiced users
ISUPPORT PREFIX=(ov)@+ must be advertised
3. MODE +m (Moderated)
Add is_moderated INTEGER NOT NULL DEFAULT 0 column to channels table
MODE #channel +m — only +o can set
When +m is active, only +v and +o users can send PRIVMSG/NOTICE to the channel
Others receive ERR_CANNOTSENDTOCHAN (404)
4. MODE +t (Topic Lock)
Add is_topic_locked INTEGER NOT NULL DEFAULT 0 column to channels table
MODE #channel +t — only +o can set
When +t is active, only +o users can change the topic
Others receive ERR_CHANOPRIVSNEEDED (482)
Default for new channels: +t ON (standard IRC behavior)
5. KICK Command
KICK #channel nick [:reason]
Only channel operators (+o) can KICK
Removes the user from the channel
Broadcasts KICK message to all channel members
Kicked user receives the KICK message
Error if kicker is not +o: ERR_CHANOPRIVSNEEDED (482)
Error if target not in channel: ERR_USERNOTINCHANNEL (441)
6. NOTICE Differentiation
NOTICE must NOT trigger AWAY auto-replies (RPL_AWAY)
NOTICE should skip hashcash validation on +H channels (servers and services use NOTICE)
NOTICE should NOT generate any auto-reply per RFC 2812
Testing
Test +o auto-grant on channel creation
Test MODE +o/+v/-o/-v with permission checks
Test +m enforcement (voiced/op can send, others can't)
Test +t enforcement (op can set topic, others can't)
Test KICK with permission checks
Test NOTICE vs PRIVMSG behavior differences
Test NAMES prefixes (@/+)
Scope
This is Tier 1 only. Tier 2 (+b bans, +i invite-only, +s secret, +k key, +l limit) comes after this is merged.
## Overview
Implement the core IRC channel functionality that users will immediately notice is missing. This is the foundation for all other mode enforcement.
## Requirements
### 1. Channel Member Flags Schema (prerequisite for everything else)
Add proper boolean columns to `channel_members` table:
```sql
ALTER TABLE channel_members ADD COLUMN is_operator INTEGER NOT NULL DEFAULT 0;
ALTER TABLE channel_members ADD COLUMN is_voiced INTEGER NOT NULL DEFAULT 0;
```
**Per sneak's instruction:** Use proper database types (boolean/integer columns), NOT a text string for modes. Each flag gets its own column.
**Channel creator auto-gets +o:** When a user JOINs and creates a new channel (ephemeral creation), they automatically get `is_operator = 1`.
### 2. MODE +o/+v/-o/-v (User Channel Modes)
- `MODE #channel +o nick` — grant operator status (only existing +o can do this)
- `MODE #channel -o nick` — revoke operator status
- `MODE #channel +v nick` — grant voice (only +o can do this)
- `MODE #channel -v nick` — revoke voice
- NAMES reply must show `@nick` for operators, `+nick` for voiced users
- ISUPPORT `PREFIX=(ov)@+` must be advertised
### 3. MODE +m (Moderated)
- Add `is_moderated INTEGER NOT NULL DEFAULT 0` column to `channels` table
- `MODE #channel +m` — only +o can set
- When +m is active, only +v and +o users can send PRIVMSG/NOTICE to the channel
- Others receive `ERR_CANNOTSENDTOCHAN` (404)
### 4. MODE +t (Topic Lock)
- Add `is_topic_locked INTEGER NOT NULL DEFAULT 0` column to `channels` table
- `MODE #channel +t` — only +o can set
- When +t is active, only +o users can change the topic
- Others receive `ERR_CHANOPRIVSNEEDED` (482)
- Default for new channels: +t ON (standard IRC behavior)
### 5. KICK Command
- `KICK #channel nick [:reason]`
- Only channel operators (+o) can KICK
- Removes the user from the channel
- Broadcasts KICK message to all channel members
- Kicked user receives the KICK message
- Error if kicker is not +o: `ERR_CHANOPRIVSNEEDED` (482)
- Error if target not in channel: `ERR_USERNOTINCHANNEL` (441)
### 6. NOTICE Differentiation
- NOTICE must NOT trigger AWAY auto-replies (RPL_AWAY)
- NOTICE should skip hashcash validation on +H channels (servers and services use NOTICE)
- NOTICE should NOT generate any auto-reply per RFC 2812
## Testing
- Test +o auto-grant on channel creation
- Test MODE +o/+v/-o/-v with permission checks
- Test +m enforcement (voiced/op can send, others can't)
- Test +t enforcement (op can set topic, others can't)
- Test KICK with permission checks
- Test NOTICE vs PRIVMSG behavior differences
- Test NAMES prefixes (@/+)
## Scope
This is Tier 1 only. [Tier 2](https://git.eeqj.de/sneak/chat/issues/86) (+b bans, +i invite-only, +s secret, +k key, +l limit) comes after this is merged.
clawbot
added this to the 1.0 milestone 2026-03-22 11:04:34 +01:00
clawbot
self-assigned this 2026-03-22 11:04:34 +01:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Overview
Implement the core IRC channel functionality that users will immediately notice is missing. This is the foundation for all other mode enforcement.
Requirements
1. Channel Member Flags Schema (prerequisite for everything else)
Add proper boolean columns to
channel_memberstable:Per sneak's instruction: Use proper database types (boolean/integer columns), NOT a text string for modes. Each flag gets its own column.
Channel creator auto-gets +o: When a user JOINs and creates a new channel (ephemeral creation), they automatically get
is_operator = 1.2. MODE +o/+v/-o/-v (User Channel Modes)
MODE #channel +o nick— grant operator status (only existing +o can do this)MODE #channel -o nick— revoke operator statusMODE #channel +v nick— grant voice (only +o can do this)MODE #channel -v nick— revoke voice@nickfor operators,+nickfor voiced usersPREFIX=(ov)@+must be advertised3. MODE +m (Moderated)
is_moderated INTEGER NOT NULL DEFAULT 0column tochannelstableMODE #channel +m— only +o can setERR_CANNOTSENDTOCHAN(404)4. MODE +t (Topic Lock)
is_topic_locked INTEGER NOT NULL DEFAULT 0column tochannelstableMODE #channel +t— only +o can setERR_CHANOPRIVSNEEDED(482)5. KICK Command
KICK #channel nick [:reason]ERR_CHANOPRIVSNEEDED(482)ERR_USERNOTINCHANNEL(441)6. NOTICE Differentiation
Testing
Scope
This is Tier 1 only. Tier 2 (+b bans, +i invite-only, +s secret, +k key, +l limit) comes after this is merged.