feat: add irc numerics package, deduplicate constants, fix dead code
All checks were successful
check / check (push) Successful in 1m5s
All checks were successful
check / check (push) Successful in 1m5s
- Create internal/irc/ package with all IRC numeric reply codes (RFC 1459/2812) and command string constants as the single source of truth - Replace all 69+ bare numeric string literals in api.go with named constants (e.g. irc.RplWelcome, irc.ErrNoSuchChannel) - Add 'code' (int) and named 'command' (e.g. RPL_YOURHOST) fields to IRC message JSON replies via irc.Name() lookup in scanMessages - Deduplicate command constants: remove local definitions from api.go, cmd/neoirc-cli/main.go, and cmd/neoirc-cli/api/client.go; all now import from internal/irc - Fix dead code: remove handleListCmd/handleWhoCmd/handleWhoisCmd/ sendWhoisNumerics that were unreachable due to dispatchCommand routing LIST/WHO/WHOIS to dispatchInfoCommand before dispatchQueryCommand. Route these commands to dispatchQueryCommand which has the improved implementations (e.g. ListAllChannelsWithCounts single-query vs N+1) - Update enqueueNumeric and respondIRCError signatures from string to int - Update test helper findNumeric to check the new 'code' JSON field Closes #52
This commit is contained in:
@@ -10,6 +10,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.eeqj.de/sneak/neoirc/internal/irc"
|
||||
"github.com/go-chi/chi"
|
||||
)
|
||||
|
||||
@@ -27,22 +28,6 @@ const (
|
||||
defaultMaxBodySize = 4096
|
||||
defaultHistLimit = 50
|
||||
maxHistLimit = 500
|
||||
cmdJoin = "JOIN"
|
||||
cmdList = "LIST"
|
||||
cmdLusers = "LUSERS"
|
||||
cmdMode = "MODE"
|
||||
cmdMotd = "MOTD"
|
||||
cmdNames = "NAMES"
|
||||
cmdNick = "NICK"
|
||||
cmdNotice = "NOTICE"
|
||||
cmdPart = "PART"
|
||||
cmdPing = "PING"
|
||||
cmdPong = "PONG"
|
||||
cmdPrivmsg = "PRIVMSG"
|
||||
cmdQuit = "QUIT"
|
||||
cmdTopic = "TOPIC"
|
||||
cmdWho = "WHO"
|
||||
cmdWhois = "WHOIS"
|
||||
)
|
||||
|
||||
func (hdlr *Handlers) maxBodySize() int64 {
|
||||
@@ -247,20 +232,20 @@ func (hdlr *Handlers) deliverWelcome(
|
||||
|
||||
// 001 RPL_WELCOME
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "001", nick, nil,
|
||||
ctx, clientID, irc.RplWelcome, nick, nil,
|
||||
"Welcome to the network, "+nick,
|
||||
)
|
||||
|
||||
// 002 RPL_YOURHOST
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "002", nick, nil,
|
||||
ctx, clientID, irc.RplYourHost, nick, nil,
|
||||
"Your host is "+srvName+
|
||||
", running version "+version,
|
||||
)
|
||||
|
||||
// 003 RPL_CREATED
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "003", nick, nil,
|
||||
ctx, clientID, irc.RplCreated, nick, nil,
|
||||
"This server was created "+
|
||||
hdlr.params.Globals.StartTime.
|
||||
Format("2006-01-02"),
|
||||
@@ -268,14 +253,14 @@ func (hdlr *Handlers) deliverWelcome(
|
||||
|
||||
// 004 RPL_MYINFO
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "004", nick,
|
||||
ctx, clientID, irc.RplMyInfo, nick,
|
||||
[]string{srvName, version, "", "imnst"},
|
||||
"",
|
||||
)
|
||||
|
||||
// 005 RPL_ISUPPORT
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "005", nick,
|
||||
ctx, clientID, irc.RplIsupport, nick,
|
||||
[]string{
|
||||
"CHANTYPES=#",
|
||||
"NICKLEN=32",
|
||||
@@ -320,7 +305,7 @@ func (hdlr *Handlers) deliverLusers(
|
||||
|
||||
// 251 RPL_LUSERCLIENT
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "251", nick, nil,
|
||||
ctx, clientID, irc.RplLuserClient, nick, nil,
|
||||
fmt.Sprintf(
|
||||
"There are %d users and 0 invisible on 1 servers",
|
||||
userCount,
|
||||
@@ -329,21 +314,21 @@ func (hdlr *Handlers) deliverLusers(
|
||||
|
||||
// 252 RPL_LUSEROP
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "252", nick,
|
||||
ctx, clientID, irc.RplLuserOp, nick,
|
||||
[]string{"0"},
|
||||
"operator(s) online",
|
||||
)
|
||||
|
||||
// 254 RPL_LUSERCHANNELS
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "254", nick,
|
||||
ctx, clientID, irc.RplLuserChannels, nick,
|
||||
[]string{strconv.FormatInt(chanCount, 10)},
|
||||
"channels formed",
|
||||
)
|
||||
|
||||
// 255 RPL_LUSERME
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "255", nick, nil,
|
||||
ctx, clientID, irc.RplLuserMe, nick, nil,
|
||||
fmt.Sprintf(
|
||||
"I have %d clients and 1 servers",
|
||||
userCount,
|
||||
@@ -381,19 +366,19 @@ func (hdlr *Handlers) deliverMOTD(
|
||||
}
|
||||
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "375", nick, nil,
|
||||
ctx, clientID, irc.RplMotdStart, nick, nil,
|
||||
"- "+srvName+" Message of the Day -",
|
||||
)
|
||||
|
||||
for line := range strings.SplitSeq(motd, "\n") {
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "372", nick, nil,
|
||||
ctx, clientID, irc.RplMotd, nick, nil,
|
||||
"- "+line,
|
||||
)
|
||||
}
|
||||
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "376", nick, nil,
|
||||
ctx, clientID, irc.RplEndOfMotd, nick, nil,
|
||||
"End of /MOTD command.",
|
||||
)
|
||||
|
||||
@@ -412,10 +397,13 @@ func (hdlr *Handlers) serverName() string {
|
||||
func (hdlr *Handlers) enqueueNumeric(
|
||||
ctx context.Context,
|
||||
clientID int64,
|
||||
command, nick string,
|
||||
code int,
|
||||
nick string,
|
||||
params []string,
|
||||
text string,
|
||||
) {
|
||||
command := fmt.Sprintf("%03d", code)
|
||||
|
||||
body, err := json.Marshal([]string{text})
|
||||
if err != nil {
|
||||
hdlr.log.Error(
|
||||
@@ -765,38 +753,38 @@ func (hdlr *Handlers) dispatchCommand(
|
||||
bodyLines func() []string,
|
||||
) {
|
||||
switch command {
|
||||
case cmdPrivmsg, cmdNotice:
|
||||
case irc.CmdPrivmsg, irc.CmdNotice:
|
||||
hdlr.handlePrivmsg(
|
||||
writer, request,
|
||||
sessionID, clientID, nick,
|
||||
command, target, body, bodyLines,
|
||||
)
|
||||
case cmdJoin:
|
||||
case irc.CmdJoin:
|
||||
hdlr.handleJoin(
|
||||
writer, request,
|
||||
sessionID, clientID, nick, target,
|
||||
)
|
||||
case cmdPart:
|
||||
case irc.CmdPart:
|
||||
hdlr.handlePart(
|
||||
writer, request,
|
||||
sessionID, clientID, nick, target, body,
|
||||
)
|
||||
case cmdNick:
|
||||
case irc.CmdNick:
|
||||
hdlr.handleNick(
|
||||
writer, request,
|
||||
sessionID, clientID, nick, bodyLines,
|
||||
)
|
||||
case cmdTopic:
|
||||
case irc.CmdTopic:
|
||||
hdlr.handleTopic(
|
||||
writer, request,
|
||||
sessionID, clientID, nick,
|
||||
target, body, bodyLines,
|
||||
)
|
||||
case cmdQuit:
|
||||
case irc.CmdQuit:
|
||||
hdlr.handleQuit(
|
||||
writer, request, sessionID, nick, body,
|
||||
)
|
||||
case cmdMotd, cmdList, cmdWho, cmdWhois, cmdPing:
|
||||
case irc.CmdMotd, irc.CmdPing:
|
||||
hdlr.dispatchInfoCommand(
|
||||
writer, request,
|
||||
sessionID, clientID, nick,
|
||||
@@ -819,34 +807,34 @@ func (hdlr *Handlers) dispatchQueryCommand(
|
||||
bodyLines func() []string,
|
||||
) {
|
||||
switch command {
|
||||
case cmdMode:
|
||||
case irc.CmdMode:
|
||||
hdlr.handleMode(
|
||||
writer, request,
|
||||
sessionID, clientID, nick,
|
||||
target, bodyLines,
|
||||
)
|
||||
case cmdNames:
|
||||
case irc.CmdNames:
|
||||
hdlr.handleNames(
|
||||
writer, request,
|
||||
sessionID, clientID, nick, target,
|
||||
)
|
||||
case cmdList:
|
||||
case irc.CmdList:
|
||||
hdlr.handleList(
|
||||
writer, request,
|
||||
sessionID, clientID, nick,
|
||||
)
|
||||
case cmdWhois:
|
||||
case irc.CmdWhois:
|
||||
hdlr.handleWhois(
|
||||
writer, request,
|
||||
sessionID, clientID, nick,
|
||||
target, bodyLines,
|
||||
)
|
||||
case cmdWho:
|
||||
case irc.CmdWho:
|
||||
hdlr.handleWho(
|
||||
writer, request,
|
||||
sessionID, clientID, nick, target,
|
||||
)
|
||||
case cmdLusers:
|
||||
case irc.CmdLusers:
|
||||
hdlr.handleLusers(
|
||||
writer, request,
|
||||
sessionID, clientID, nick,
|
||||
@@ -854,7 +842,7 @@ func (hdlr *Handlers) dispatchQueryCommand(
|
||||
default:
|
||||
hdlr.enqueueNumeric(
|
||||
request.Context(), clientID,
|
||||
"421", nick, []string{command},
|
||||
irc.ErrUnknownCommand, nick, []string{command},
|
||||
"Unknown command",
|
||||
)
|
||||
hdlr.broker.Notify(sessionID)
|
||||
@@ -875,7 +863,7 @@ func (hdlr *Handlers) handlePrivmsg(
|
||||
if target == "" {
|
||||
hdlr.enqueueNumeric(
|
||||
request.Context(), clientID,
|
||||
"461", nick, []string{command},
|
||||
irc.ErrNeedMoreParams, nick, []string{command},
|
||||
"Not enough parameters",
|
||||
)
|
||||
hdlr.broker.Notify(sessionID)
|
||||
@@ -890,7 +878,7 @@ func (hdlr *Handlers) handlePrivmsg(
|
||||
if len(lines) == 0 {
|
||||
hdlr.enqueueNumeric(
|
||||
request.Context(), clientID,
|
||||
"461", nick, []string{command},
|
||||
irc.ErrNeedMoreParams, nick, []string{command},
|
||||
"Not enough parameters",
|
||||
)
|
||||
hdlr.broker.Notify(sessionID)
|
||||
@@ -924,13 +912,14 @@ func (hdlr *Handlers) respondIRCError(
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
clientID, sessionID int64,
|
||||
numeric, nick string,
|
||||
code int,
|
||||
nick string,
|
||||
params []string,
|
||||
text string,
|
||||
) {
|
||||
hdlr.enqueueNumeric(
|
||||
request.Context(), clientID,
|
||||
numeric, nick, params, text,
|
||||
code, nick, params, text,
|
||||
)
|
||||
hdlr.broker.Notify(sessionID)
|
||||
hdlr.respondJSON(writer, request,
|
||||
@@ -951,7 +940,7 @@ func (hdlr *Handlers) handleChannelMsg(
|
||||
if err != nil {
|
||||
hdlr.respondIRCError(
|
||||
writer, request, clientID, sessionID,
|
||||
"403", nick, []string{target},
|
||||
irc.ErrNoSuchChannel, nick, []string{target},
|
||||
"No such channel",
|
||||
)
|
||||
|
||||
@@ -977,7 +966,7 @@ func (hdlr *Handlers) handleChannelMsg(
|
||||
if !isMember {
|
||||
hdlr.respondIRCError(
|
||||
writer, request, clientID, sessionID,
|
||||
"442", nick, []string{target},
|
||||
irc.ErrNotOnChannel, nick, []string{target},
|
||||
"You're not on that channel",
|
||||
)
|
||||
|
||||
@@ -1044,7 +1033,7 @@ func (hdlr *Handlers) handleDirectMsg(
|
||||
if err != nil {
|
||||
hdlr.enqueueNumeric(
|
||||
request.Context(), clientID,
|
||||
"401", nick, []string{target},
|
||||
irc.ErrNoSuchNick, nick, []string{target},
|
||||
"No such nick/channel",
|
||||
)
|
||||
hdlr.broker.Notify(sessionID)
|
||||
@@ -1088,7 +1077,7 @@ func (hdlr *Handlers) handleJoin(
|
||||
if target == "" {
|
||||
hdlr.respondIRCError(
|
||||
writer, request, clientID, sessionID,
|
||||
"461", nick, []string{cmdJoin},
|
||||
irc.ErrNeedMoreParams, nick, []string{irc.CmdJoin},
|
||||
"Not enough parameters",
|
||||
)
|
||||
|
||||
@@ -1103,7 +1092,7 @@ func (hdlr *Handlers) handleJoin(
|
||||
if !validChannelRe.MatchString(channel) {
|
||||
hdlr.respondIRCError(
|
||||
writer, request, clientID, sessionID,
|
||||
"403", nick, []string{channel},
|
||||
irc.ErrNoSuchChannel, nick, []string{channel},
|
||||
"No such channel",
|
||||
)
|
||||
|
||||
@@ -1159,7 +1148,7 @@ func (hdlr *Handlers) executeJoin(
|
||||
)
|
||||
|
||||
_ = hdlr.fanOutSilent(
|
||||
request, cmdJoin, nick, channel, nil, memberIDs,
|
||||
request, irc.CmdJoin, nick, channel, nil, memberIDs,
|
||||
)
|
||||
|
||||
hdlr.deliverJoinNumerics(
|
||||
@@ -1210,12 +1199,12 @@ func (hdlr *Handlers) deliverJoinNumerics(
|
||||
|
||||
if topic != "" {
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "332", nick,
|
||||
ctx, clientID, irc.RplTopic, nick,
|
||||
[]string{channel}, topic,
|
||||
)
|
||||
} else {
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "331", nick,
|
||||
ctx, clientID, irc.RplNoTopic, nick,
|
||||
[]string{channel}, "No topic is set",
|
||||
)
|
||||
}
|
||||
@@ -1233,14 +1222,14 @@ func (hdlr *Handlers) deliverJoinNumerics(
|
||||
}
|
||||
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "353", nick,
|
||||
ctx, clientID, irc.RplNamReply, nick,
|
||||
[]string{"=", channel},
|
||||
strings.Join(nicks, " "),
|
||||
)
|
||||
}
|
||||
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "366", nick,
|
||||
ctx, clientID, irc.RplEndOfNames, nick,
|
||||
[]string{channel}, "End of /NAMES list",
|
||||
)
|
||||
|
||||
@@ -1257,7 +1246,7 @@ func (hdlr *Handlers) handlePart(
|
||||
if target == "" {
|
||||
hdlr.enqueueNumeric(
|
||||
request.Context(), clientID,
|
||||
"461", nick, []string{cmdPart},
|
||||
irc.ErrNeedMoreParams, nick, []string{irc.CmdPart},
|
||||
"Not enough parameters",
|
||||
)
|
||||
hdlr.broker.Notify(sessionID)
|
||||
@@ -1279,7 +1268,7 @@ func (hdlr *Handlers) handlePart(
|
||||
if err != nil {
|
||||
hdlr.enqueueNumeric(
|
||||
request.Context(), clientID,
|
||||
"403", nick, []string{channel},
|
||||
irc.ErrNoSuchChannel, nick, []string{channel},
|
||||
"No such channel",
|
||||
)
|
||||
hdlr.broker.Notify(sessionID)
|
||||
@@ -1295,7 +1284,7 @@ func (hdlr *Handlers) handlePart(
|
||||
)
|
||||
|
||||
_ = hdlr.fanOutSilent(
|
||||
request, cmdPart, nick, channel, body, memberIDs,
|
||||
request, irc.CmdPart, nick, channel, body, memberIDs,
|
||||
)
|
||||
|
||||
err = hdlr.params.Database.PartChannel(
|
||||
@@ -1337,7 +1326,7 @@ func (hdlr *Handlers) handleNick(
|
||||
if len(lines) == 0 {
|
||||
hdlr.respondIRCError(
|
||||
writer, request, clientID, sessionID,
|
||||
"461", nick, []string{cmdNick},
|
||||
irc.ErrNeedMoreParams, nick, []string{irc.CmdNick},
|
||||
"Not enough parameters",
|
||||
)
|
||||
|
||||
@@ -1349,7 +1338,7 @@ func (hdlr *Handlers) handleNick(
|
||||
if !validNickRe.MatchString(newNick) {
|
||||
hdlr.respondIRCError(
|
||||
writer, request, clientID, sessionID,
|
||||
"432", nick, []string{newNick},
|
||||
irc.ErrErroneusNickname, nick, []string{newNick},
|
||||
"Erroneous nickname",
|
||||
)
|
||||
|
||||
@@ -1385,7 +1374,7 @@ func (hdlr *Handlers) executeNickChange(
|
||||
if strings.Contains(err.Error(), "UNIQUE") {
|
||||
hdlr.respondIRCError(
|
||||
writer, request, clientID, sessionID,
|
||||
"433", nick, []string{newNick},
|
||||
irc.ErrNicknameInUse, nick, []string{newNick},
|
||||
"Nickname is already in use",
|
||||
)
|
||||
|
||||
@@ -1435,7 +1424,7 @@ func (hdlr *Handlers) broadcastNick(
|
||||
}
|
||||
|
||||
dbID, _, _ := hdlr.params.Database.InsertMessage(
|
||||
request.Context(), cmdNick, oldNick, "",
|
||||
request.Context(), irc.CmdNick, oldNick, "",
|
||||
nil, json.RawMessage(nickBody), nil,
|
||||
)
|
||||
|
||||
@@ -1476,7 +1465,7 @@ func (hdlr *Handlers) handleTopic(
|
||||
if target == "" {
|
||||
hdlr.respondIRCError(
|
||||
writer, request, clientID, sessionID,
|
||||
"461", nick, []string{cmdTopic},
|
||||
irc.ErrNeedMoreParams, nick, []string{irc.CmdTopic},
|
||||
"Not enough parameters",
|
||||
)
|
||||
|
||||
@@ -1487,7 +1476,7 @@ func (hdlr *Handlers) handleTopic(
|
||||
if len(lines) == 0 {
|
||||
hdlr.respondIRCError(
|
||||
writer, request, clientID, sessionID,
|
||||
"461", nick, []string{cmdTopic},
|
||||
irc.ErrNeedMoreParams, nick, []string{irc.CmdTopic},
|
||||
"Not enough parameters",
|
||||
)
|
||||
|
||||
@@ -1505,7 +1494,7 @@ func (hdlr *Handlers) handleTopic(
|
||||
if err != nil {
|
||||
hdlr.respondIRCError(
|
||||
writer, request, clientID, sessionID,
|
||||
"403", nick, []string{channel},
|
||||
irc.ErrNoSuchChannel, nick, []string{channel},
|
||||
"No such channel",
|
||||
)
|
||||
|
||||
@@ -1549,12 +1538,12 @@ func (hdlr *Handlers) executeTopic(
|
||||
)
|
||||
|
||||
_ = hdlr.fanOutSilent(
|
||||
request, cmdTopic, nick, channel, body, memberIDs,
|
||||
request, irc.CmdTopic, nick, channel, body, memberIDs,
|
||||
)
|
||||
|
||||
hdlr.enqueueNumeric(
|
||||
request.Context(), clientID,
|
||||
"332", nick, []string{channel}, topic,
|
||||
irc.RplTopic, nick, []string{channel}, topic,
|
||||
)
|
||||
hdlr.broker.Notify(sessionID)
|
||||
|
||||
@@ -1566,8 +1555,7 @@ func (hdlr *Handlers) executeTopic(
|
||||
}
|
||||
|
||||
// dispatchInfoCommand handles informational IRC commands
|
||||
// that produce server-side numerics (MOTD, LIST, WHO,
|
||||
// WHOIS, PING).
|
||||
// that produce server-side numerics (MOTD, PING).
|
||||
func (hdlr *Handlers) dispatchInfoCommand(
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
@@ -1575,31 +1563,20 @@ func (hdlr *Handlers) dispatchInfoCommand(
|
||||
nick, command, target string,
|
||||
bodyLines func() []string,
|
||||
) {
|
||||
_ = target
|
||||
_ = bodyLines
|
||||
|
||||
okResp := map[string]string{"status": "ok"}
|
||||
|
||||
switch command {
|
||||
case cmdMotd:
|
||||
case irc.CmdMotd:
|
||||
hdlr.deliverMOTD(
|
||||
request, clientID, sessionID, nick,
|
||||
)
|
||||
case cmdList:
|
||||
hdlr.handleListCmd(
|
||||
request, clientID, sessionID, nick,
|
||||
)
|
||||
case cmdWho:
|
||||
hdlr.handleWhoCmd(
|
||||
request, clientID, sessionID, nick,
|
||||
target,
|
||||
)
|
||||
case cmdWhois:
|
||||
hdlr.handleWhoisCmd(
|
||||
request, clientID, sessionID, nick,
|
||||
target, bodyLines,
|
||||
)
|
||||
case cmdPing:
|
||||
case irc.CmdPing:
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{
|
||||
"command": cmdPong,
|
||||
"command": irc.CmdPong,
|
||||
"from": hdlr.serverName(),
|
||||
},
|
||||
http.StatusOK)
|
||||
@@ -1612,222 +1589,6 @@ func (hdlr *Handlers) dispatchInfoCommand(
|
||||
)
|
||||
}
|
||||
|
||||
// handleListCmd sends RPL_LIST (322) for each channel,
|
||||
// then sends 323 to signal the end of the list.
|
||||
func (hdlr *Handlers) handleListCmd(
|
||||
request *http.Request,
|
||||
clientID, sessionID int64,
|
||||
nick string,
|
||||
) {
|
||||
ctx := request.Context()
|
||||
|
||||
channels, err := hdlr.params.Database.ListAllChannels(
|
||||
ctx,
|
||||
)
|
||||
if err != nil {
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "323", nick, nil,
|
||||
"End of /LIST",
|
||||
)
|
||||
hdlr.broker.Notify(sessionID)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
for _, channel := range channels {
|
||||
memberIDs, _ :=
|
||||
hdlr.params.Database.GetChannelMemberIDs(
|
||||
ctx, channel.ID,
|
||||
)
|
||||
|
||||
count := strconv.Itoa(len(memberIDs))
|
||||
topic := channel.Topic
|
||||
|
||||
if topic == "" {
|
||||
topic = " "
|
||||
}
|
||||
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "322", nick,
|
||||
[]string{channel.Name, count}, topic,
|
||||
)
|
||||
}
|
||||
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "323", nick, nil,
|
||||
"End of /LIST",
|
||||
)
|
||||
hdlr.broker.Notify(sessionID)
|
||||
}
|
||||
|
||||
// handleWhoCmd sends RPL_WHOREPLY (352) for each member
|
||||
// of the target channel, followed by RPL_ENDOFWHO (315).
|
||||
func (hdlr *Handlers) handleWhoCmd(
|
||||
request *http.Request,
|
||||
clientID, sessionID int64,
|
||||
nick, target string,
|
||||
) {
|
||||
ctx := request.Context()
|
||||
|
||||
if target == "" {
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "461", nick,
|
||||
[]string{cmdWho}, "Not enough parameters",
|
||||
)
|
||||
hdlr.broker.Notify(sessionID)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
channel := target
|
||||
if !strings.HasPrefix(channel, "#") {
|
||||
channel = "#" + channel
|
||||
}
|
||||
|
||||
chID, err := hdlr.params.Database.GetChannelByName(
|
||||
ctx, channel,
|
||||
)
|
||||
if err != nil {
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "315", nick,
|
||||
[]string{channel}, "End of /WHO list",
|
||||
)
|
||||
hdlr.broker.Notify(sessionID)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
members, err := hdlr.params.Database.ChannelMembers(
|
||||
ctx, chID,
|
||||
)
|
||||
if err == nil {
|
||||
srvName := hdlr.serverName()
|
||||
|
||||
for _, mem := range members {
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "352", nick,
|
||||
[]string{
|
||||
channel, mem.Nick, "neoirc",
|
||||
srvName, mem.Nick, "H",
|
||||
},
|
||||
"0 "+mem.Nick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "315", nick,
|
||||
[]string{channel}, "End of /WHO list",
|
||||
)
|
||||
hdlr.broker.Notify(sessionID)
|
||||
}
|
||||
|
||||
// handleWhoisCmd sends WHOIS reply numerics (311, 312,
|
||||
// 319, 318) for the target nick.
|
||||
func (hdlr *Handlers) handleWhoisCmd(
|
||||
request *http.Request,
|
||||
clientID, sessionID int64,
|
||||
nick, target string,
|
||||
bodyLines func() []string,
|
||||
) {
|
||||
ctx := request.Context()
|
||||
|
||||
whoisNick := target
|
||||
if whoisNick == "" {
|
||||
lines := bodyLines()
|
||||
if len(lines) > 0 {
|
||||
whoisNick = strings.TrimSpace(lines[0])
|
||||
}
|
||||
}
|
||||
|
||||
if whoisNick == "" {
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "461", nick,
|
||||
[]string{cmdWhois}, "Not enough parameters",
|
||||
)
|
||||
hdlr.broker.Notify(sessionID)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
targetSID, err :=
|
||||
hdlr.params.Database.GetSessionByNick(
|
||||
ctx, whoisNick,
|
||||
)
|
||||
if err != nil {
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "401", nick,
|
||||
[]string{whoisNick},
|
||||
"No such nick/channel",
|
||||
)
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "318", nick,
|
||||
[]string{whoisNick},
|
||||
"End of /WHOIS list",
|
||||
)
|
||||
hdlr.broker.Notify(sessionID)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
hdlr.sendWhoisNumerics(
|
||||
ctx, clientID, sessionID, nick,
|
||||
whoisNick, targetSID,
|
||||
)
|
||||
}
|
||||
|
||||
// sendWhoisNumerics emits 311/312/319/318 for a
|
||||
// resolved WHOIS target.
|
||||
func (hdlr *Handlers) sendWhoisNumerics(
|
||||
ctx context.Context,
|
||||
clientID, sessionID int64,
|
||||
nick, whoisNick string,
|
||||
targetSID int64,
|
||||
) {
|
||||
srvName := hdlr.serverName()
|
||||
|
||||
// 311 RPL_WHOISUSER
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "311", nick,
|
||||
[]string{whoisNick, whoisNick, "neoirc", "*"},
|
||||
whoisNick,
|
||||
)
|
||||
|
||||
// 312 RPL_WHOISSERVER
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "312", nick,
|
||||
[]string{whoisNick, srvName},
|
||||
srvName,
|
||||
)
|
||||
|
||||
// 319 RPL_WHOISCHANNELS
|
||||
channels, _ := hdlr.params.Database.GetSessionChannels(
|
||||
ctx, targetSID,
|
||||
)
|
||||
|
||||
if len(channels) > 0 {
|
||||
names := make([]string, 0, len(channels))
|
||||
|
||||
for _, chanInfo := range channels {
|
||||
names = append(names, chanInfo.Name)
|
||||
}
|
||||
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "319", nick,
|
||||
[]string{whoisNick},
|
||||
strings.Join(names, " "),
|
||||
)
|
||||
}
|
||||
|
||||
// 318 RPL_ENDOFWHOIS
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "318", nick,
|
||||
[]string{whoisNick},
|
||||
"End of /WHOIS list",
|
||||
)
|
||||
hdlr.broker.Notify(sessionID)
|
||||
}
|
||||
|
||||
func (hdlr *Handlers) handleQuit(
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
@@ -1846,7 +1607,7 @@ func (hdlr *Handlers) handleQuit(
|
||||
|
||||
if len(channels) > 0 {
|
||||
dbID, _, _ = hdlr.params.Database.InsertMessage(
|
||||
request.Context(), cmdQuit, nick, "",
|
||||
request.Context(), irc.CmdQuit, nick, "",
|
||||
nil, body, nil,
|
||||
)
|
||||
}
|
||||
@@ -1899,7 +1660,7 @@ func (hdlr *Handlers) handleMode(
|
||||
if target == "" {
|
||||
hdlr.respondIRCError(
|
||||
writer, request, clientID, sessionID,
|
||||
"461", nick, []string{cmdMode},
|
||||
irc.ErrNeedMoreParams, nick, []string{irc.CmdMode},
|
||||
"Not enough parameters",
|
||||
)
|
||||
|
||||
@@ -1911,7 +1672,7 @@ func (hdlr *Handlers) handleMode(
|
||||
// User mode query — return empty modes.
|
||||
hdlr.enqueueNumeric(
|
||||
request.Context(), clientID,
|
||||
"221", nick, nil, "+",
|
||||
irc.RplUmodeIs, nick, nil, "+",
|
||||
)
|
||||
hdlr.broker.Notify(sessionID)
|
||||
hdlr.respondJSON(writer, request,
|
||||
@@ -1943,7 +1704,7 @@ func (hdlr *Handlers) handleChannelMode(
|
||||
if err != nil {
|
||||
hdlr.respondIRCError(
|
||||
writer, request, clientID, sessionID,
|
||||
"403", nick, []string{channel},
|
||||
irc.ErrNoSuchChannel, nick, []string{channel},
|
||||
"No such channel",
|
||||
)
|
||||
|
||||
@@ -1952,7 +1713,7 @@ func (hdlr *Handlers) handleChannelMode(
|
||||
|
||||
// 324 RPL_CHANNELMODEIS
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "324", nick,
|
||||
ctx, clientID, irc.RplChannelModeIs, nick,
|
||||
[]string{channel, "+n"}, "",
|
||||
)
|
||||
|
||||
@@ -1961,7 +1722,7 @@ func (hdlr *Handlers) handleChannelMode(
|
||||
GetChannelCreatedAt(ctx, chID)
|
||||
if timeErr == nil {
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "329", nick,
|
||||
ctx, clientID, irc.RplCreationTime, nick,
|
||||
[]string{
|
||||
channel,
|
||||
strconv.FormatInt(
|
||||
@@ -1988,7 +1749,7 @@ func (hdlr *Handlers) handleNames(
|
||||
if target == "" {
|
||||
hdlr.respondIRCError(
|
||||
writer, request, clientID, sessionID,
|
||||
"461", nick, []string{cmdNames},
|
||||
irc.ErrNeedMoreParams, nick, []string{irc.CmdNames},
|
||||
"Not enough parameters",
|
||||
)
|
||||
|
||||
@@ -2008,7 +1769,7 @@ func (hdlr *Handlers) handleNames(
|
||||
if err != nil {
|
||||
hdlr.respondIRCError(
|
||||
writer, request, clientID, sessionID,
|
||||
"403", nick, []string{channel},
|
||||
irc.ErrNoSuchChannel, nick, []string{channel},
|
||||
"No such channel",
|
||||
)
|
||||
|
||||
@@ -2026,14 +1787,14 @@ func (hdlr *Handlers) handleNames(
|
||||
}
|
||||
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "353", nick,
|
||||
ctx, clientID, irc.RplNamReply, nick,
|
||||
[]string{"=", channel},
|
||||
strings.Join(nicks, " "),
|
||||
)
|
||||
}
|
||||
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "366", nick,
|
||||
ctx, clientID, irc.RplEndOfNames, nick,
|
||||
[]string{channel}, "End of /NAMES list",
|
||||
)
|
||||
|
||||
@@ -2071,7 +1832,7 @@ func (hdlr *Handlers) handleList(
|
||||
for _, chanInfo := range channels {
|
||||
// 322 RPL_LIST
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "322", nick,
|
||||
ctx, clientID, irc.RplList, nick,
|
||||
[]string{
|
||||
chanInfo.Name,
|
||||
strconv.FormatInt(
|
||||
@@ -2084,7 +1845,7 @@ func (hdlr *Handlers) handleList(
|
||||
|
||||
// 323 — end of channel list.
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "323", nick, nil,
|
||||
ctx, clientID, irc.RplListEnd, nick, nil,
|
||||
"End of /LIST",
|
||||
)
|
||||
|
||||
@@ -2115,7 +1876,7 @@ func (hdlr *Handlers) handleWhois(
|
||||
if queryNick == "" {
|
||||
hdlr.respondIRCError(
|
||||
writer, request, clientID, sessionID,
|
||||
"461", nick, []string{cmdWhois},
|
||||
irc.ErrNeedMoreParams, nick, []string{irc.CmdWhois},
|
||||
"Not enough parameters",
|
||||
)
|
||||
|
||||
@@ -2142,12 +1903,12 @@ func (hdlr *Handlers) executeWhois(
|
||||
)
|
||||
if err != nil {
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "401", nick,
|
||||
ctx, clientID, irc.ErrNoSuchNick, nick,
|
||||
[]string{queryNick},
|
||||
"No such nick/channel",
|
||||
)
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "318", nick,
|
||||
ctx, clientID, irc.RplEndOfWhois, nick,
|
||||
[]string{queryNick},
|
||||
"End of /WHOIS list",
|
||||
)
|
||||
@@ -2161,14 +1922,14 @@ func (hdlr *Handlers) executeWhois(
|
||||
|
||||
// 311 RPL_WHOISUSER
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "311", nick,
|
||||
ctx, clientID, irc.RplWhoisUser, nick,
|
||||
[]string{queryNick, queryNick, srvName, "*"},
|
||||
queryNick,
|
||||
)
|
||||
|
||||
// 312 RPL_WHOISSERVER
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "312", nick,
|
||||
ctx, clientID, irc.RplWhoisServer, nick,
|
||||
[]string{queryNick, srvName},
|
||||
"neoirc server",
|
||||
)
|
||||
@@ -2180,7 +1941,7 @@ func (hdlr *Handlers) executeWhois(
|
||||
|
||||
// 318 RPL_ENDOFWHOIS
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "318", nick,
|
||||
ctx, clientID, irc.RplEndOfWhois, nick,
|
||||
[]string{queryNick},
|
||||
"End of /WHOIS list",
|
||||
)
|
||||
@@ -2210,7 +1971,7 @@ func (hdlr *Handlers) deliverWhoisChannels(
|
||||
}
|
||||
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "319", nick,
|
||||
ctx, clientID, irc.RplWhoisChannels, nick,
|
||||
[]string{queryNick},
|
||||
strings.Join(chanNames, " "),
|
||||
)
|
||||
@@ -2226,7 +1987,7 @@ func (hdlr *Handlers) handleWho(
|
||||
if target == "" {
|
||||
hdlr.respondIRCError(
|
||||
writer, request, clientID, sessionID,
|
||||
"461", nick, []string{cmdWho},
|
||||
irc.ErrNeedMoreParams, nick, []string{irc.CmdWho},
|
||||
"Not enough parameters",
|
||||
)
|
||||
|
||||
@@ -2247,7 +2008,7 @@ func (hdlr *Handlers) handleWho(
|
||||
if err != nil {
|
||||
// 315 RPL_ENDOFWHO (empty result)
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "315", nick,
|
||||
ctx, clientID, irc.RplEndOfWho, nick,
|
||||
[]string{target},
|
||||
"End of /WHO list",
|
||||
)
|
||||
@@ -2266,7 +2027,7 @@ func (hdlr *Handlers) handleWho(
|
||||
for _, mem := range members {
|
||||
// 352 RPL_WHOREPLY
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "352", nick,
|
||||
ctx, clientID, irc.RplWhoReply, nick,
|
||||
[]string{
|
||||
channel, mem.Nick, srvName,
|
||||
srvName, mem.Nick, "H",
|
||||
@@ -2278,7 +2039,7 @@ func (hdlr *Handlers) handleWho(
|
||||
|
||||
// 315 RPL_ENDOFWHO
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "315", nick,
|
||||
ctx, clientID, irc.RplEndOfWho, nick,
|
||||
[]string{channel},
|
||||
"End of /WHO list",
|
||||
)
|
||||
@@ -2514,7 +2275,7 @@ func (hdlr *Handlers) cleanupUser(
|
||||
|
||||
if len(channels) > 0 {
|
||||
quitDBID, _, _ = hdlr.params.Database.InsertMessage(
|
||||
ctx, cmdQuit, nick, "",
|
||||
ctx, irc.CmdQuit, nick, "",
|
||||
nil, nil, nil,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
@@ -467,8 +468,11 @@ func findNumeric(
|
||||
msgs []map[string]any,
|
||||
numeric string,
|
||||
) bool {
|
||||
want, _ := strconv.Atoi(numeric)
|
||||
|
||||
for _, msg := range msgs {
|
||||
if msg[commandKey] == numeric {
|
||||
code, ok := msg["code"].(float64)
|
||||
if ok && int(code) == want {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user