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:
@@ -13,15 +13,14 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.eeqj.de/sneak/neoirc/internal/irc"
|
||||
)
|
||||
|
||||
const (
|
||||
httpTimeout = 30 * time.Second
|
||||
pollExtraTime = 5
|
||||
httpErrThreshold = 400
|
||||
|
||||
cmdJoin = "JOIN"
|
||||
cmdPart = "PART"
|
||||
)
|
||||
|
||||
var errHTTP = errors.New("HTTP error")
|
||||
@@ -171,7 +170,7 @@ func (client *Client) PollMessages(
|
||||
func (client *Client) JoinChannel(channel string) error {
|
||||
return client.SendMessage(
|
||||
&Message{ //nolint:exhaustruct // only command+to needed
|
||||
Command: cmdJoin, To: channel,
|
||||
Command: irc.CmdJoin, To: channel,
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -180,7 +179,7 @@ func (client *Client) JoinChannel(channel string) error {
|
||||
func (client *Client) PartChannel(channel string) error {
|
||||
return client.SendMessage(
|
||||
&Message{ //nolint:exhaustruct // only command+to needed
|
||||
Command: cmdPart, To: channel,
|
||||
Command: irc.CmdPart, To: channel,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
api "git.eeqj.de/sneak/neoirc/cmd/neoirc-cli/api"
|
||||
"git.eeqj.de/sneak/neoirc/internal/irc"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -16,17 +17,6 @@ const (
|
||||
pollTimeout = 15
|
||||
pollRetry = 2 * time.Second
|
||||
timeFormat = "15:04"
|
||||
|
||||
cmdJoin = "JOIN"
|
||||
cmdMotd = "MOTD"
|
||||
cmdNick = "NICK"
|
||||
cmdNotice = "NOTICE"
|
||||
cmdPart = "PART"
|
||||
cmdPrivmsg = "PRIVMSG"
|
||||
cmdQuit = "QUIT"
|
||||
cmdTopic = "TOPIC"
|
||||
cmdWho = "WHO"
|
||||
cmdWhois = "WHOIS"
|
||||
)
|
||||
|
||||
// App holds the application state.
|
||||
@@ -97,7 +87,7 @@ func (a *App) handleInput(text string) {
|
||||
}
|
||||
|
||||
err := a.client.SendMessage(&api.Message{ //nolint:exhaustruct
|
||||
Command: cmdPrivmsg,
|
||||
Command: irc.CmdPrivmsg,
|
||||
To: target,
|
||||
Body: []string{text},
|
||||
})
|
||||
@@ -252,7 +242,7 @@ func (a *App) cmdNick(nick string) {
|
||||
}
|
||||
|
||||
err := a.client.SendMessage(&api.Message{ //nolint:exhaustruct
|
||||
Command: cmdNick,
|
||||
Command: irc.CmdNick,
|
||||
Body: []string{nick},
|
||||
})
|
||||
if err != nil {
|
||||
@@ -387,7 +377,7 @@ func (a *App) cmdMsg(args string) {
|
||||
}
|
||||
|
||||
err := a.client.SendMessage(&api.Message{ //nolint:exhaustruct
|
||||
Command: cmdPrivmsg,
|
||||
Command: irc.CmdPrivmsg,
|
||||
To: target,
|
||||
Body: []string{text},
|
||||
})
|
||||
@@ -445,7 +435,7 @@ func (a *App) cmdTopic(args string) {
|
||||
|
||||
if args == "" {
|
||||
err := a.client.SendMessage(&api.Message{ //nolint:exhaustruct
|
||||
Command: cmdTopic,
|
||||
Command: irc.CmdTopic,
|
||||
To: target,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -458,7 +448,7 @@ func (a *App) cmdTopic(args string) {
|
||||
}
|
||||
|
||||
err := a.client.SendMessage(&api.Message{ //nolint:exhaustruct
|
||||
Command: cmdTopic,
|
||||
Command: irc.CmdTopic,
|
||||
To: target,
|
||||
Body: []string{args},
|
||||
})
|
||||
@@ -546,7 +536,7 @@ func (a *App) cmdMotd() {
|
||||
}
|
||||
|
||||
err := a.client.SendMessage(
|
||||
&api.Message{Command: cmdMotd}, //nolint:exhaustruct
|
||||
&api.Message{Command: irc.CmdMotd}, //nolint:exhaustruct
|
||||
)
|
||||
if err != nil {
|
||||
a.ui.AddStatus(fmt.Sprintf(
|
||||
@@ -583,7 +573,7 @@ func (a *App) cmdWho(args string) {
|
||||
|
||||
err := a.client.SendMessage(
|
||||
&api.Message{ //nolint:exhaustruct
|
||||
Command: cmdWho, To: channel,
|
||||
Command: irc.CmdWho, To: channel,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
@@ -614,7 +604,7 @@ func (a *App) cmdWhois(args string) {
|
||||
|
||||
err := a.client.SendMessage(
|
||||
&api.Message{ //nolint:exhaustruct
|
||||
Command: cmdWhois, To: args,
|
||||
Command: irc.CmdWhois, To: args,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
@@ -664,7 +654,7 @@ func (a *App) cmdQuit() {
|
||||
|
||||
if a.connected && a.client != nil {
|
||||
_ = a.client.SendMessage(
|
||||
&api.Message{Command: cmdQuit}, //nolint:exhaustruct
|
||||
&api.Message{Command: irc.CmdQuit}, //nolint:exhaustruct
|
||||
)
|
||||
}
|
||||
|
||||
@@ -749,19 +739,19 @@ func (a *App) handleServerMessage(msg *api.Message) {
|
||||
a.mu.Unlock()
|
||||
|
||||
switch msg.Command {
|
||||
case cmdPrivmsg:
|
||||
case irc.CmdPrivmsg:
|
||||
a.handlePrivmsgEvent(msg, timestamp, myNick)
|
||||
case cmdJoin:
|
||||
case irc.CmdJoin:
|
||||
a.handleJoinEvent(msg, timestamp)
|
||||
case cmdPart:
|
||||
case irc.CmdPart:
|
||||
a.handlePartEvent(msg, timestamp)
|
||||
case cmdQuit:
|
||||
case irc.CmdQuit:
|
||||
a.handleQuitEvent(msg, timestamp)
|
||||
case cmdNick:
|
||||
case irc.CmdNick:
|
||||
a.handleNickEvent(msg, timestamp, myNick)
|
||||
case cmdNotice:
|
||||
case irc.CmdNotice:
|
||||
a.handleNoticeEvent(msg, timestamp)
|
||||
case cmdTopic:
|
||||
case irc.CmdTopic:
|
||||
a.handleTopicEvent(msg, timestamp)
|
||||
default:
|
||||
a.handleDefaultEvent(msg, timestamp)
|
||||
|
||||
@@ -7,8 +7,10 @@ import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"git.eeqj.de/sneak/neoirc/internal/irc"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
@@ -33,6 +35,7 @@ func generateToken() (string, error) {
|
||||
type IRCMessage struct {
|
||||
ID string `json:"id"`
|
||||
Command string `json:"command"`
|
||||
Code int `json:"code,omitempty"`
|
||||
From string `json:"from,omitempty"`
|
||||
To string `json:"to,omitempty"`
|
||||
Params json.RawMessage `json:"params,omitempty"`
|
||||
@@ -42,6 +45,15 @@ type IRCMessage struct {
|
||||
DBID int64 `json:"-"`
|
||||
}
|
||||
|
||||
// isNumericCode returns true if s is exactly a 3-digit
|
||||
// IRC numeric reply code.
|
||||
func isNumericCode(s string) bool {
|
||||
return len(s) == 3 &&
|
||||
s[0] >= '0' && s[0] <= '9' &&
|
||||
s[1] >= '0' && s[1] <= '9' &&
|
||||
s[2] >= '0' && s[2] <= '9'
|
||||
}
|
||||
|
||||
// ChannelInfo is a lightweight channel representation.
|
||||
type ChannelInfo struct {
|
||||
ID int64 `json:"id"`
|
||||
@@ -717,6 +729,15 @@ func scanMessages(
|
||||
msg.DBID = qID
|
||||
lastQID = qID
|
||||
|
||||
if isNumericCode(msg.Command) {
|
||||
code, _ := strconv.Atoi(msg.Command)
|
||||
msg.Code = code
|
||||
|
||||
if name := irc.Name(code); name != "" {
|
||||
msg.Command = name
|
||||
}
|
||||
}
|
||||
|
||||
msgs = append(msgs, msg)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
21
internal/irc/commands.go
Normal file
21
internal/irc/commands.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package irc
|
||||
|
||||
// IRC command names (RFC 1459 / RFC 2812).
|
||||
const (
|
||||
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"
|
||||
)
|
||||
150
internal/irc/numerics.go
Normal file
150
internal/irc/numerics.go
Normal file
@@ -0,0 +1,150 @@
|
||||
// Package irc provides constants and utilities for the
|
||||
// IRC protocol, including numeric reply codes from
|
||||
// RFC 1459 and RFC 2812, and standard command names.
|
||||
package irc
|
||||
|
||||
// Connection registration replies (001-005).
|
||||
const (
|
||||
RplWelcome = 1
|
||||
RplYourHost = 2
|
||||
RplCreated = 3
|
||||
RplMyInfo = 4
|
||||
RplIsupport = 5
|
||||
)
|
||||
|
||||
// Command responses (200-399).
|
||||
const (
|
||||
RplUmodeIs = 221
|
||||
RplLuserClient = 251
|
||||
RplLuserOp = 252
|
||||
RplLuserUnknown = 253
|
||||
RplLuserChannels = 254
|
||||
RplLuserMe = 255
|
||||
RplAway = 301
|
||||
RplUserHost = 302
|
||||
RplIson = 303
|
||||
RplUnaway = 305
|
||||
RplNowAway = 306
|
||||
RplWhoisUser = 311
|
||||
RplWhoisServer = 312
|
||||
RplWhoisOperator = 313
|
||||
RplEndOfWho = 315
|
||||
RplWhoisIdle = 317
|
||||
RplEndOfWhois = 318
|
||||
RplWhoisChannels = 319
|
||||
RplList = 322
|
||||
RplListEnd = 323
|
||||
RplChannelModeIs = 324
|
||||
RplCreationTime = 329
|
||||
RplNoTopic = 331
|
||||
RplTopic = 332
|
||||
RplTopicWhoTime = 333
|
||||
RplInviting = 341
|
||||
RplWhoReply = 352
|
||||
RplNamReply = 353
|
||||
RplEndOfNames = 366
|
||||
RplBanList = 367
|
||||
RplEndOfBanList = 368
|
||||
RplMotd = 372
|
||||
RplMotdStart = 375
|
||||
RplEndOfMotd = 376
|
||||
)
|
||||
|
||||
// Error replies (400-599).
|
||||
const (
|
||||
ErrNoSuchNick = 401
|
||||
ErrNoSuchServer = 402
|
||||
ErrNoSuchChannel = 403
|
||||
ErrCannotSendToChan = 404
|
||||
ErrTooManyChannels = 405
|
||||
ErrNoRecipient = 411
|
||||
ErrNoTextToSend = 412
|
||||
ErrUnknownCommand = 421
|
||||
ErrNoNicknameGiven = 431
|
||||
ErrErroneusNickname = 432
|
||||
ErrNicknameInUse = 433
|
||||
ErrUserNotInChannel = 441
|
||||
ErrNotOnChannel = 442
|
||||
ErrNotRegistered = 451
|
||||
ErrNeedMoreParams = 461
|
||||
ErrAlreadyRegistered = 462
|
||||
ErrChannelIsFull = 471
|
||||
ErrInviteOnlyChan = 473
|
||||
ErrBannedFromChan = 474
|
||||
ErrBadChannelKey = 475
|
||||
ErrChanOpPrivsNeeded = 482
|
||||
)
|
||||
|
||||
// names maps numeric codes to their standard IRC names.
|
||||
//
|
||||
//nolint:gochecknoglobals
|
||||
var names = map[int]string{
|
||||
RplWelcome: "RPL_WELCOME",
|
||||
RplYourHost: "RPL_YOURHOST",
|
||||
RplCreated: "RPL_CREATED",
|
||||
RplMyInfo: "RPL_MYINFO",
|
||||
RplIsupport: "RPL_ISUPPORT",
|
||||
RplUmodeIs: "RPL_UMODEIS",
|
||||
RplLuserClient: "RPL_LUSERCLIENT",
|
||||
RplLuserOp: "RPL_LUSEROP",
|
||||
RplLuserUnknown: "RPL_LUSERUNKNOWN",
|
||||
RplLuserChannels: "RPL_LUSERCHANNELS",
|
||||
RplLuserMe: "RPL_LUSERME",
|
||||
RplAway: "RPL_AWAY",
|
||||
RplUserHost: "RPL_USERHOST",
|
||||
RplIson: "RPL_ISON",
|
||||
RplUnaway: "RPL_UNAWAY",
|
||||
RplNowAway: "RPL_NOWAWAY",
|
||||
RplWhoisUser: "RPL_WHOISUSER",
|
||||
RplWhoisServer: "RPL_WHOISSERVER",
|
||||
RplWhoisOperator: "RPL_WHOISOPERATOR",
|
||||
RplEndOfWho: "RPL_ENDOFWHO",
|
||||
RplWhoisIdle: "RPL_WHOISIDLE",
|
||||
RplEndOfWhois: "RPL_ENDOFWHOIS",
|
||||
RplWhoisChannels: "RPL_WHOISCHANNELS",
|
||||
RplList: "RPL_LIST",
|
||||
RplListEnd: "RPL_LISTEND", //nolint:misspell
|
||||
RplChannelModeIs: "RPL_CHANNELMODEIS",
|
||||
RplCreationTime: "RPL_CREATIONTIME",
|
||||
RplNoTopic: "RPL_NOTOPIC",
|
||||
RplTopic: "RPL_TOPIC",
|
||||
RplTopicWhoTime: "RPL_TOPICWHOTIME",
|
||||
RplInviting: "RPL_INVITING",
|
||||
RplWhoReply: "RPL_WHOREPLY",
|
||||
RplNamReply: "RPL_NAMREPLY",
|
||||
RplEndOfNames: "RPL_ENDOFNAMES",
|
||||
RplBanList: "RPL_BANLIST",
|
||||
RplEndOfBanList: "RPL_ENDOFBANLIST",
|
||||
RplMotd: "RPL_MOTD",
|
||||
RplMotdStart: "RPL_MOTDSTART",
|
||||
RplEndOfMotd: "RPL_ENDOFMOTD",
|
||||
|
||||
ErrNoSuchNick: "ERR_NOSUCHNICK",
|
||||
ErrNoSuchServer: "ERR_NOSUCHSERVER",
|
||||
ErrNoSuchChannel: "ERR_NOSUCHCHANNEL",
|
||||
ErrCannotSendToChan: "ERR_CANNOTSENDTOCHAN",
|
||||
ErrTooManyChannels: "ERR_TOOMANYCHANNELS",
|
||||
ErrNoRecipient: "ERR_NORECIPIENT",
|
||||
ErrNoTextToSend: "ERR_NOTEXTTOSEND",
|
||||
ErrUnknownCommand: "ERR_UNKNOWNCOMMAND",
|
||||
ErrNoNicknameGiven: "ERR_NONICKNAMEGIVEN",
|
||||
ErrErroneusNickname: "ERR_ERRONEUSNICKNAME",
|
||||
ErrNicknameInUse: "ERR_NICKNAMEINUSE",
|
||||
ErrUserNotInChannel: "ERR_USERNOTINCHANNEL",
|
||||
ErrNotOnChannel: "ERR_NOTONCHANNEL",
|
||||
ErrNotRegistered: "ERR_NOTREGISTERED",
|
||||
ErrNeedMoreParams: "ERR_NEEDMOREPARAMS",
|
||||
ErrAlreadyRegistered: "ERR_ALREADYREGISTERED",
|
||||
ErrChannelIsFull: "ERR_CHANNELISFULL",
|
||||
ErrInviteOnlyChan: "ERR_INVITEONLYCHAN",
|
||||
ErrBannedFromChan: "ERR_BANNEDFROMCHAN",
|
||||
ErrBadChannelKey: "ERR_BADCHANNELKEY",
|
||||
ErrChanOpPrivsNeeded: "ERR_CHANOPRIVSNEEDED",
|
||||
}
|
||||
|
||||
// Name returns the standard IRC name for a numeric code
|
||||
// (e.g., Name(2) returns "RPL_YOURHOST"). Returns an
|
||||
// empty string if the code is unknown.
|
||||
func Name(code int) string {
|
||||
return names[code]
|
||||
}
|
||||
Reference in New Issue
Block a user