2 Commits

Author SHA1 Message Date
user
48072cd26e docs: fix RPL_MYINFO example in README to match code (ikmnostl)
Some checks failed
check / check (push) Failing after 1m35s
2026-03-25 12:17:43 -07:00
user
75867ba778 feat: implement Tier 2 channel modes (+b/+i/+s/+k/+l)
Implement the second tier of IRC channel features:

1. Ban system (+b): Add/remove/list bans with wildcard matching.
   Bans prevent both joining and sending messages.
   Schema: channel_bans table with mask, set_by, created_at.

2. Invite-only (+i): Channel mode requiring invitation to join.
   INVITE command for operators. Invites stored in DB and
   cleared after successful JOIN.

3. Secret (+s): Hides channel from LIST for non-members and
   from WHOIS channel lists when querier is not in same channel.

4. Channel key (+k): Password-protected channels. Key required
   on JOIN, set/cleared by operators.

5. User limit (+l): Maximum member count enforcement. Rejects
   JOIN when channel is at capacity.

Updated ISUPPORT CHANMODES to b,k,Hl,imnst.
Updated RPL_MYINFO available modes to ikmnostl.
Comprehensive tests for all features at both DB and handler levels.
README updated with full documentation of all new modes.

closes #86
2026-03-25 12:17:43 -07:00
2 changed files with 5 additions and 24 deletions

View File

@@ -135,21 +135,13 @@ type migration struct {
func (database *Database) runMigrations( func (database *Database) runMigrations(
ctx context.Context, ctx context.Context,
) error { ) error {
bootstrap, err := SchemaFiles.ReadFile( _, err := database.conn.ExecContext(ctx,
"schema/000.sql", `CREATE TABLE IF NOT EXISTS schema_migrations (
) version INTEGER PRIMARY KEY,
applied_at DATETIME DEFAULT CURRENT_TIMESTAMP)`)
if err != nil { if err != nil {
return fmt.Errorf( return fmt.Errorf(
"read bootstrap migration: %w", err, "create schema_migrations: %w", err,
)
}
_, err = database.conn.ExecContext(
ctx, string(bootstrap),
)
if err != nil {
return fmt.Errorf(
"execute bootstrap migration: %w", err,
) )
} }
@@ -278,11 +270,6 @@ func (database *Database) loadMigrations() (
continue continue
} }
// Skip bootstrap migration; it is executed separately.
if version == 0 {
continue
}
content, readErr := SchemaFiles.ReadFile( content, readErr := SchemaFiles.ReadFile(
"schema/" + entry.Name(), "schema/" + entry.Name(),
) )

View File

@@ -1,6 +0,0 @@
-- Bootstrap: create the schema_migrations table itself.
CREATE TABLE IF NOT EXISTS schema_migrations (
version INTEGER PRIMARY KEY,
applied_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
INSERT OR IGNORE INTO schema_migrations (version) VALUES (0);