feat: implement Tier 3 utility IRC commands
All checks were successful
check / check (push) Successful in 59s
All checks were successful
check / check (push) Successful in 59s
Implement all 7 utility IRC commands from issue #87: User commands: - USERHOST: quick lookup of user@host for up to 5 nicks (RPL 302) - VERSION: server version string using globals.Version (RPL 351) - ADMIN: server admin contact info (RPL 256-259) - INFO: server software info text (RPL 371/374) - TIME: server local time in RFC format (RPL 391) Oper commands: - KILL: forcibly disconnect a user (requires is_oper), broadcasts QUIT to all shared channels, cleans up sessions - WALLOPS: broadcast message to all users with +w usermode (requires is_oper) Supporting changes: - Add is_wallops column to sessions table in 001_initial.sql - Add user mode +w tracking via MODE nick +w/-w - User mode queries now return actual modes (+o, +w) - MODE -o allows de-opering yourself; MODE +o rejected - MODE for other users returns ERR_USERSDONTMATCH (502) - Extract dispatch helpers to reduce dispatchCommand complexity Tests cover all commands including error cases, oper checks, user mode set/unset, KILL broadcast, WALLOPS delivery, and edge cases (self-kill, nonexistent users, missing params). closes #87
This commit is contained in:
@@ -1014,10 +1014,12 @@ func (hdlr *Handlers) dispatchCommand(
|
||||
bodyLines func() []string,
|
||||
) {
|
||||
switch command {
|
||||
case irc.CmdAway:
|
||||
hdlr.handleAway(
|
||||
case irc.CmdAway, irc.CmdNick,
|
||||
irc.CmdPass, irc.CmdInvite:
|
||||
hdlr.dispatchBodyOnlyCommand(
|
||||
writer, request,
|
||||
sessionID, clientID, nick, bodyLines,
|
||||
sessionID, clientID, nick,
|
||||
command, bodyLines,
|
||||
)
|
||||
case irc.CmdPrivmsg, irc.CmdNotice:
|
||||
hdlr.handlePrivmsg(
|
||||
@@ -1036,27 +1038,12 @@ func (hdlr *Handlers) dispatchCommand(
|
||||
writer, request,
|
||||
sessionID, clientID, nick, target, body,
|
||||
)
|
||||
case irc.CmdNick:
|
||||
hdlr.handleNick(
|
||||
writer, request,
|
||||
sessionID, clientID, nick, bodyLines,
|
||||
)
|
||||
case irc.CmdPass:
|
||||
hdlr.handlePass(
|
||||
writer, request,
|
||||
sessionID, clientID, nick, bodyLines,
|
||||
)
|
||||
case irc.CmdTopic:
|
||||
hdlr.handleTopic(
|
||||
writer, request,
|
||||
sessionID, clientID, nick,
|
||||
target, body, bodyLines,
|
||||
)
|
||||
case irc.CmdInvite:
|
||||
hdlr.handleInvite(
|
||||
writer, request,
|
||||
sessionID, clientID, nick, bodyLines,
|
||||
)
|
||||
case irc.CmdKick:
|
||||
hdlr.handleKick(
|
||||
writer, request,
|
||||
@@ -1067,12 +1054,15 @@ func (hdlr *Handlers) dispatchCommand(
|
||||
hdlr.handleQuit(
|
||||
writer, request, sessionID, nick, body,
|
||||
)
|
||||
case irc.CmdOper:
|
||||
hdlr.handleOper(
|
||||
case irc.CmdOper, irc.CmdKill, irc.CmdWallops:
|
||||
hdlr.dispatchOperCommand(
|
||||
writer, request,
|
||||
sessionID, clientID, nick, bodyLines,
|
||||
sessionID, clientID, nick,
|
||||
command, bodyLines,
|
||||
)
|
||||
case irc.CmdMotd, irc.CmdPing:
|
||||
case irc.CmdMotd, irc.CmdPing,
|
||||
irc.CmdVersion, irc.CmdAdmin,
|
||||
irc.CmdInfo, irc.CmdTime:
|
||||
hdlr.dispatchInfoCommand(
|
||||
writer, request,
|
||||
sessionID, clientID, nick,
|
||||
@@ -1127,6 +1117,11 @@ func (hdlr *Handlers) dispatchQueryCommand(
|
||||
writer, request,
|
||||
sessionID, clientID, nick,
|
||||
)
|
||||
case irc.CmdUserhost:
|
||||
hdlr.handleUserhost(
|
||||
writer, request,
|
||||
sessionID, clientID, nick, bodyLines,
|
||||
)
|
||||
default:
|
||||
hdlr.enqueueNumeric(
|
||||
request.Context(), clientID,
|
||||
@@ -2416,7 +2411,8 @@ func (hdlr *Handlers) executeTopic(
|
||||
}
|
||||
|
||||
// dispatchInfoCommand handles informational IRC commands
|
||||
// that produce server-side numerics (MOTD, PING).
|
||||
// that produce server-side numerics (MOTD, PING,
|
||||
// VERSION, ADMIN, INFO, TIME).
|
||||
func (hdlr *Handlers) dispatchInfoCommand(
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
@@ -2442,6 +2438,34 @@ func (hdlr *Handlers) dispatchInfoCommand(
|
||||
},
|
||||
http.StatusOK)
|
||||
|
||||
return
|
||||
case irc.CmdVersion:
|
||||
hdlr.handleVersion(
|
||||
writer, request,
|
||||
sessionID, clientID, nick,
|
||||
)
|
||||
|
||||
return
|
||||
case irc.CmdAdmin:
|
||||
hdlr.handleAdmin(
|
||||
writer, request,
|
||||
sessionID, clientID, nick,
|
||||
)
|
||||
|
||||
return
|
||||
case irc.CmdInfo:
|
||||
hdlr.handleInfo(
|
||||
writer, request,
|
||||
sessionID, clientID, nick,
|
||||
)
|
||||
|
||||
return
|
||||
case irc.CmdTime:
|
||||
hdlr.handleTime(
|
||||
writer, request,
|
||||
sessionID, clientID, nick,
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -2532,15 +2556,11 @@ func (hdlr *Handlers) handleMode(
|
||||
|
||||
channel := target
|
||||
if !strings.HasPrefix(channel, "#") {
|
||||
// User mode query — return empty modes.
|
||||
hdlr.enqueueNumeric(
|
||||
request.Context(), clientID,
|
||||
irc.RplUmodeIs, nick, nil, "+",
|
||||
hdlr.handleUserMode(
|
||||
writer, request,
|
||||
sessionID, clientID, nick, target,
|
||||
bodyLines,
|
||||
)
|
||||
hdlr.broker.Notify(sessionID)
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "ok"},
|
||||
http.StatusOK)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user