feat: implement Tier 1 channel modes (+o/+v/+m/+t), KICK, NOTICE #88
Reference in New Issue
Block a user
Delete Branch "feature/tier1-channel-modes-85"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Implement the core IRC channel functionality that users will immediately notice is missing. This is the foundation for all other mode enforcement.
closes #85
Changes
1. Channel Member Flags Schema
is_operator INTEGER NOT NULL DEFAULT 0andis_voiced INTEGER NOT NULL DEFAULT 0columns tochannel_memberstable2. MODE +o/+v/-o/-v (User Channel Modes)
MODE #channel +o nick/-o/+v/-vwith permission checks+ousers can grant/revoke modes@nickfor operators,+nickfor voiced usersPREFIX=(ov)@+3. MODE +m (Moderated)
is_moderated INTEGER NOT NULL DEFAULT 0tochannelstableERR_CANNOTSENDTOCHAN(404)4. MODE +t (Topic Lock)
is_topic_locked INTEGER NOT NULL DEFAULT 1tochannelstableERR_CHANOPRIVSNEEDED(482)5. KICK Command
KICK #channel nick [:reason]— operator-only6. NOTICE Differentiation
Additional Improvements
PREFIX=(ov)@+,CHANMODES=,,H,mnstTesting
22 new tests covering:
CI
docker build .passes — lint (0 issues), fmt-check, and all tests green.Review: PR #88 — Tier 1 Channel Modes (+o/+v/+m/+t), KICK, NOTICE
Verdict: FAIL
Build Status
docker build .passes — lint (0 issues), fmt-check, and all 22 new tests green. No build regressions.Requirements Checklist
is_operator INTEGER NOT NULL DEFAULT 0onchannel_members— ✅ schema correctis_voiced INTEGER NOT NULL DEFAULT 0onchannel_members— ✅ schema correct+oon first JOIN — ✅CountChannelMembers→JoinChannelAsOperator+o— ✅requireChannelOpgate@nickand+nickprefixes — ✅memberPrefix()functionPREFIX=(ov)@+— ✅ indeliverWelcome005 paramsis_moderated INTEGER NOT NULL DEFAULT 0on channels — ✅ schema correct+menforcement: only+o/+vcan send, others getERR_CANNOTSENDTOCHAN— ✅checkModeratedSendis_topic_locked INTEGER NOT NULL DEFAULT 1on channels — ✅ schema correct, default +t ON+tenforcement: only+ocan change topic — ✅executeTopicchecksIsChannelTopicLocked+ocheck, target removal, broadcast — ✅ full implementation withvalidateKick/broadcastKickcommand != irc.CmdNoticegate inhandleDirectMsg+Hchannels — ✅isNoticecheck beforevalidateChannelHashcashBlocking Issue: RPL_MYINFO (004) code/README mismatch
The README at line 1082 was updated to show:
But the actual code at
api.go:450still sends:The README says
"mnst"(noi), but the server sends"imnst"(withi). This is a factual documentation error — the README doesn't match the server's actual behavior.Furthermore, this creates an internal inconsistency within the code itself:
imnst(includesi),,H,mnst(does NOT includei)The same server simultaneously claims it supports
+iin one place and doesn't in another. The fix should be to updateapi.go:450to"mnst"to match both the README and the CHANMODES string.Code Quality Notes (non-blocking)
Overall, the implementation is well-structured. Specific positives:
requireChannelOp,resolveUserModeTarget,setChannelFlagare well-factored helpersfanOutSilentcleanup (removing unusedmetaparam) is a nice drive-by fixTestModeratedNoticeBlocked)fanOut) to support theparamsfield for target nick — correct approachMinor Observations (not blocking, for awareness)
+sin CHANMODES but unenforced:+s(secret) is still advertised inCHANMODES=,,H,mnstand RPL_MYINFO, but is documented as "Not yet enforced" and has no server-side handling. A client sendingMODE #channel +swould get an unknown mode error. This is pre-existing but worth noting since+iwas deliberately removed for the same reason.Missing
441and404from numeric reply reference table: The new KICK feature sends441(ERR_USERNOTINCHANNEL) and +m enforcement sends404(ERR_CANNOTSENDTOCHAN), but neither appears in the numeric codes reference table (lines 1080-1118). The KICK section documents 441, but the centralized table should be comprehensive for client developers. (404 absence is pre-existing.)Pre-existing:
setHashcashMode/clearHashcashModehave no operator check: The new mode handlers all correctly userequireChannelOp, but the pre-existing +H/-H handlers at lines 2798/2862 have no op check. Any channel member can set/clear hashcash. Not introduced by this PR.Summary
The implementation is solid — schema design, permission model, tests, and KICK broadcast logic are all correct. The single blocking issue is the RPL_MYINFO code/README mismatch at
api.go:450where"imnst"should be"mnst". One-line fix.Rework Complete
Fixed the RPL_MYINFO (004) channel mode string at
api.go:450:"imnst"(includedifor invite-only, which is Tier 2 / #86)"mnst"(matches CHANMODES in 005 and README)docker build .passes — lint, fmt-check, and all tests green.Re-review: PR #88 — RPL_MYINFO fix
Verdict: PASS ✅
Fix Verified
api.go:450now sends"mnst"(was"imnst").Consistency Check
api.go:450mnstapi.go:461,,H,mnstmnstCHANMODES=,,H,mnstAll four references are consistent.
+i(invite-only) correctly excluded — it's Tier 2 (#86).Build
docker build .passes — lint (0 issues), fmt-check, and all tests green. The rework commit (3a6fc92) is a single-line diff, no regressions.Summary
The blocking issue from the previous review is resolved. The full Tier 1 implementation (22 tests, MODE +o/+v/+m/+t, KICK, NOTICE) remains solid. Ready to merge.
“make test” fails
I just ran
make teston the PR branch (3a6fc92) — all tests pass, exit code 0. Also verified main (08f57bc) passes.Could you paste the error output? I want to make sure I'm reproducing the same failure. Possible causes:
web/dist/placeholder files (the PR branch has anensure-web-distMakefile target that creates them automatically)build is failing in gitea ci. how about “docker build .” on a fresh checkout?
Gitea CI shows commit
3a6fc92failed after 2m48s. However, I randocker build --no-cache .on a fresh clone of the PR branch and it passes cleanly:I can't access the CI job logs (API returns 403 for Actions, and the web UI requires owner auth). Could you paste the CI failure output? Possible CI-specific causes:
docker build .