diff --git a/internal/db/queries.go b/internal/db/queries.go index 0698ef0..3a36a9b 100644 --- a/internal/db/queries.go +++ b/internal/db/queries.go @@ -746,8 +746,8 @@ func scanMessages( code, _ := strconv.Atoi(msg.Command) msg.Code = code - if name := irc.Name(irc.MessageType(code)); name != "" { - msg.Command = name + if mt, err := irc.FromInt(code); err == nil { + msg.Command = mt.Name() } } diff --git a/internal/handlers/api.go b/internal/handlers/api.go index 3762cf8..8dd41aa 100644 --- a/internal/handlers/api.go +++ b/internal/handlers/api.go @@ -398,12 +398,12 @@ func (hdlr *Handlers) serverName() string { func (hdlr *Handlers) enqueueNumeric( ctx context.Context, clientID int64, - code irc.MessageType, + code irc.IRCMessageType, nick string, params []string, text string, ) { - command := code.String() + command := code.Code() body, err := json.Marshal([]string{text}) if err != nil { @@ -969,7 +969,7 @@ func (hdlr *Handlers) respondIRCError( writer http.ResponseWriter, request *http.Request, clientID, sessionID int64, - code irc.MessageType, + code irc.IRCMessageType, nick string, params []string, text string, diff --git a/pkg/irc/numerics.go b/pkg/irc/numerics.go index 73d3bc2..b71ebc2 100644 --- a/pkg/irc/numerics.go +++ b/pkg/irc/numerics.go @@ -3,194 +3,228 @@ // RFC 1459 and RFC 2812, and standard command names. package irc -import "fmt" +import ( + "errors" + "fmt" +) -// MessageType represents an IRC numeric reply or error code. -type MessageType int +// IRCMessageType represents an IRC numeric reply or error code. +type IRCMessageType int //nolint:revive // Name requested by project owner. // Name returns the standard IRC name for this numeric code -// (e.g., MessageType(2).Name() returns "RPL_YOURHOST"). +// (e.g., IRCMessageType(252).Name() returns "RPL_LUSEROP"). // Returns an empty string if the code is unknown. -func (t MessageType) Name() string { +func (t IRCMessageType) Name() string { return names[t] } -// String returns the three-digit zero-padded string representation -// of the numeric code (e.g., MessageType(1).String() returns "001"). -func (t MessageType) String() string { +// String returns the name and numeric code in angle brackets +// (e.g., IRCMessageType(252).String() returns "RPL_LUSEROP <252>"). +// If the code is unknown, returns "UNKNOWN ". +func (t IRCMessageType) String() string { + n := names[t] + if n == "" { + n = "UNKNOWN" + } + + return fmt.Sprintf("%s <%03d>", n, int(t)) +} + +// Code returns the three-digit zero-padded string representation +// of the numeric code (e.g., IRCMessageType(252).Code() returns "252"). +func (t IRCMessageType) Code() string { return fmt.Sprintf("%03d", int(t)) } +// Int returns the bare integer value of the numeric code. +func (t IRCMessageType) Int() int { + return int(t) +} + +// ErrUnknownNumeric is returned by FromInt when the numeric code is not recognized. +var ErrUnknownNumeric = errors.New("unknown IRC numeric code") + +// FromInt converts an integer to an IRCMessageType, returning an error +// if the numeric code is not a known IRC reply or error code. +func FromInt(n int) (IRCMessageType, error) { + t := IRCMessageType(n) + if _, ok := names[t]; !ok { + return 0, fmt.Errorf("%w: %d", ErrUnknownNumeric, n) + } + + return t, nil +} + // Connection registration replies (001-005). const ( - RplWelcome MessageType = 1 - RplYourHost MessageType = 2 - RplCreated MessageType = 3 - RplMyInfo MessageType = 4 - RplBounce MessageType = 5 // RFC 2812; also known as RPL_ISUPPORT in practice - RplIsupport MessageType = 5 // De-facto standard (same numeric as RplBounce) + RplWelcome IRCMessageType = 1 + RplYourHost IRCMessageType = 2 + RplCreated IRCMessageType = 3 + RplMyInfo IRCMessageType = 4 + RplBounce IRCMessageType = 5 // RFC 2812; also known as RPL_ISUPPORT in practice + RplIsupport IRCMessageType = 5 // De-facto standard (same numeric as RplBounce) ) // Command responses (200-399). const ( // RFC 2812 trace/stats/links replies (200-219). - RplTraceLink MessageType = 200 - RplTraceConnecting MessageType = 201 - RplTraceHandshake MessageType = 202 - RplTraceUnknown MessageType = 203 - RplTraceOperator MessageType = 204 - RplTraceUser MessageType = 205 - RplTraceServer MessageType = 206 - RplTraceService MessageType = 207 - RplTraceNewType MessageType = 208 - RplTraceClass MessageType = 209 - RplStatsLinkInfo MessageType = 211 - RplStatsCommands MessageType = 212 - RplStatsCLine MessageType = 213 - RplStatsNLine MessageType = 214 - RplStatsILine MessageType = 215 - RplStatsKLine MessageType = 216 - RplStatsQLine MessageType = 217 - RplStatsYLine MessageType = 218 - RplEndOfStats MessageType = 219 + RplTraceLink IRCMessageType = 200 + RplTraceConnecting IRCMessageType = 201 + RplTraceHandshake IRCMessageType = 202 + RplTraceUnknown IRCMessageType = 203 + RplTraceOperator IRCMessageType = 204 + RplTraceUser IRCMessageType = 205 + RplTraceServer IRCMessageType = 206 + RplTraceService IRCMessageType = 207 + RplTraceNewType IRCMessageType = 208 + RplTraceClass IRCMessageType = 209 + RplStatsLinkInfo IRCMessageType = 211 + RplStatsCommands IRCMessageType = 212 + RplStatsCLine IRCMessageType = 213 + RplStatsNLine IRCMessageType = 214 + RplStatsILine IRCMessageType = 215 + RplStatsKLine IRCMessageType = 216 + RplStatsQLine IRCMessageType = 217 + RplStatsYLine IRCMessageType = 218 + RplEndOfStats IRCMessageType = 219 - RplUmodeIs MessageType = 221 - RplServList MessageType = 234 - RplServListEnd MessageType = 235 - RplStatsLLine MessageType = 241 - RplStatsUptime MessageType = 242 - RplStatsOLine MessageType = 243 - RplStatsHLine MessageType = 244 - RplLuserClient MessageType = 251 - RplLuserOp MessageType = 252 - RplLuserUnknown MessageType = 253 + RplUmodeIs IRCMessageType = 221 + RplServList IRCMessageType = 234 + RplServListEnd IRCMessageType = 235 + RplStatsLLine IRCMessageType = 241 + RplStatsUptime IRCMessageType = 242 + RplStatsOLine IRCMessageType = 243 + RplStatsHLine IRCMessageType = 244 + RplLuserClient IRCMessageType = 251 + RplLuserOp IRCMessageType = 252 + RplLuserUnknown IRCMessageType = 253 - RplLuserChannels MessageType = 254 - RplLuserMe MessageType = 255 - RplAdminMe MessageType = 256 - RplAdminLoc1 MessageType = 257 - RplAdminLoc2 MessageType = 258 - RplAdminEmail MessageType = 259 - RplTraceLog MessageType = 261 - RplTraceEnd MessageType = 262 - RplTryAgain MessageType = 263 + RplLuserChannels IRCMessageType = 254 + RplLuserMe IRCMessageType = 255 + RplAdminMe IRCMessageType = 256 + RplAdminLoc1 IRCMessageType = 257 + RplAdminLoc2 IRCMessageType = 258 + RplAdminEmail IRCMessageType = 259 + RplTraceLog IRCMessageType = 261 + RplTraceEnd IRCMessageType = 262 + RplTryAgain IRCMessageType = 263 - RplAway MessageType = 301 - RplUserHost MessageType = 302 - RplIson MessageType = 303 - RplUnaway MessageType = 305 - RplNowAway MessageType = 306 - RplWhoisUser MessageType = 311 - RplWhoisServer MessageType = 312 - RplWhoisOperator MessageType = 313 - RplWhoWasUser MessageType = 314 - RplEndOfWho MessageType = 315 - RplWhoisIdle MessageType = 317 - RplEndOfWhois MessageType = 318 - RplWhoisChannels MessageType = 319 - RplListStart MessageType = 321 - RplList MessageType = 322 - RplListEnd MessageType = 323 - RplChannelModeIs MessageType = 324 + RplAway IRCMessageType = 301 + RplUserHost IRCMessageType = 302 + RplIson IRCMessageType = 303 + RplUnaway IRCMessageType = 305 + RplNowAway IRCMessageType = 306 + RplWhoisUser IRCMessageType = 311 + RplWhoisServer IRCMessageType = 312 + RplWhoisOperator IRCMessageType = 313 + RplWhoWasUser IRCMessageType = 314 + RplEndOfWho IRCMessageType = 315 + RplWhoisIdle IRCMessageType = 317 + RplEndOfWhois IRCMessageType = 318 + RplWhoisChannels IRCMessageType = 319 + RplListStart IRCMessageType = 321 + RplList IRCMessageType = 322 + RplListEnd IRCMessageType = 323 + RplChannelModeIs IRCMessageType = 324 - RplUniqOpIs MessageType = 325 - RplCreationTime MessageType = 329 - RplNoTopic MessageType = 331 - RplTopic MessageType = 332 - RplTopicWhoTime MessageType = 333 - RplInviting MessageType = 341 - RplSummoning MessageType = 342 - RplInviteList MessageType = 346 - RplEndOfInviteList MessageType = 347 - RplExceptList MessageType = 348 - RplEndOfExceptList MessageType = 349 - RplVersion MessageType = 351 - RplWhoReply MessageType = 352 - RplNamReply MessageType = 353 - RplLinks MessageType = 364 - RplEndOfLinks MessageType = 365 - RplEndOfNames MessageType = 366 - RplBanList MessageType = 367 - RplEndOfBanList MessageType = 368 - RplEndOfWhowas MessageType = 369 - RplInfo MessageType = 371 - RplMotd MessageType = 372 - RplEndOfInfo MessageType = 374 - RplMotdStart MessageType = 375 - RplEndOfMotd MessageType = 376 - RplYoureOper MessageType = 381 - RplRehashing MessageType = 382 - RplYoureService MessageType = 383 - RplTime MessageType = 391 - RplUsersStart MessageType = 392 - RplUsers MessageType = 393 - RplEndOfUsers MessageType = 394 - RplNoUsers MessageType = 395 + RplUniqOpIs IRCMessageType = 325 + RplCreationTime IRCMessageType = 329 + RplNoTopic IRCMessageType = 331 + RplTopic IRCMessageType = 332 + RplTopicWhoTime IRCMessageType = 333 + RplInviting IRCMessageType = 341 + RplSummoning IRCMessageType = 342 + RplInviteList IRCMessageType = 346 + RplEndOfInviteList IRCMessageType = 347 + RplExceptList IRCMessageType = 348 + RplEndOfExceptList IRCMessageType = 349 + RplVersion IRCMessageType = 351 + RplWhoReply IRCMessageType = 352 + RplNamReply IRCMessageType = 353 + RplLinks IRCMessageType = 364 + RplEndOfLinks IRCMessageType = 365 + RplEndOfNames IRCMessageType = 366 + RplBanList IRCMessageType = 367 + RplEndOfBanList IRCMessageType = 368 + RplEndOfWhowas IRCMessageType = 369 + RplInfo IRCMessageType = 371 + RplMotd IRCMessageType = 372 + RplEndOfInfo IRCMessageType = 374 + RplMotdStart IRCMessageType = 375 + RplEndOfMotd IRCMessageType = 376 + RplYoureOper IRCMessageType = 381 + RplRehashing IRCMessageType = 382 + RplYoureService IRCMessageType = 383 + RplTime IRCMessageType = 391 + RplUsersStart IRCMessageType = 392 + RplUsers IRCMessageType = 393 + RplEndOfUsers IRCMessageType = 394 + RplNoUsers IRCMessageType = 395 ) // Error replies (400-599). const ( - ErrNoSuchNick MessageType = 401 - ErrNoSuchServer MessageType = 402 - ErrNoSuchChannel MessageType = 403 - ErrCannotSendToChan MessageType = 404 - ErrTooManyChannels MessageType = 405 - ErrWasNoSuchNick MessageType = 406 - ErrTooManyTargets MessageType = 407 - ErrNoSuchService MessageType = 408 - ErrNoOrigin MessageType = 409 - ErrNoRecipient MessageType = 411 - ErrNoTextToSend MessageType = 412 - ErrNoTopLevel MessageType = 413 - ErrWildTopLevel MessageType = 414 - ErrBadMask MessageType = 415 - ErrUnknownCommand MessageType = 421 - ErrNoMotd MessageType = 422 - ErrNoAdminInfo MessageType = 423 - ErrFileError MessageType = 424 - ErrNoNicknameGiven MessageType = 431 - ErrErroneusNickname MessageType = 432 - ErrNicknameInUse MessageType = 433 - ErrNickCollision MessageType = 436 - ErrUnavailResource MessageType = 437 + ErrNoSuchNick IRCMessageType = 401 + ErrNoSuchServer IRCMessageType = 402 + ErrNoSuchChannel IRCMessageType = 403 + ErrCannotSendToChan IRCMessageType = 404 + ErrTooManyChannels IRCMessageType = 405 + ErrWasNoSuchNick IRCMessageType = 406 + ErrTooManyTargets IRCMessageType = 407 + ErrNoSuchService IRCMessageType = 408 + ErrNoOrigin IRCMessageType = 409 + ErrNoRecipient IRCMessageType = 411 + ErrNoTextToSend IRCMessageType = 412 + ErrNoTopLevel IRCMessageType = 413 + ErrWildTopLevel IRCMessageType = 414 + ErrBadMask IRCMessageType = 415 + ErrUnknownCommand IRCMessageType = 421 + ErrNoMotd IRCMessageType = 422 + ErrNoAdminInfo IRCMessageType = 423 + ErrFileError IRCMessageType = 424 + ErrNoNicknameGiven IRCMessageType = 431 + ErrErroneusNickname IRCMessageType = 432 + ErrNicknameInUse IRCMessageType = 433 + ErrNickCollision IRCMessageType = 436 + ErrUnavailResource IRCMessageType = 437 - ErrUserNotInChannel MessageType = 441 - ErrNotOnChannel MessageType = 442 - ErrUserOnChannel MessageType = 443 - ErrNoLogin MessageType = 444 - ErrSummonDisabled MessageType = 445 - ErrUsersDisabled MessageType = 446 - ErrNotRegistered MessageType = 451 - ErrNeedMoreParams MessageType = 461 - ErrAlreadyRegistered MessageType = 462 - ErrNoPermForHost MessageType = 463 - ErrPasswdMismatch MessageType = 464 - ErrYoureBannedCreep MessageType = 465 - ErrYouWillBeBanned MessageType = 466 - ErrKeySet MessageType = 467 - ErrChannelIsFull MessageType = 471 - ErrUnknownMode MessageType = 472 - ErrInviteOnlyChan MessageType = 473 - ErrBannedFromChan MessageType = 474 - ErrBadChannelKey MessageType = 475 - ErrBadChanMask MessageType = 476 - ErrNoChanModes MessageType = 477 - ErrBanListFull MessageType = 478 - ErrNoPrivileges MessageType = 481 - ErrChanOpPrivsNeeded MessageType = 482 - ErrCantKillServer MessageType = 483 - ErrRestricted MessageType = 484 - ErrUniqOpPrivsNeeded MessageType = 485 - ErrNoOperHost MessageType = 491 + ErrUserNotInChannel IRCMessageType = 441 + ErrNotOnChannel IRCMessageType = 442 + ErrUserOnChannel IRCMessageType = 443 + ErrNoLogin IRCMessageType = 444 + ErrSummonDisabled IRCMessageType = 445 + ErrUsersDisabled IRCMessageType = 446 + ErrNotRegistered IRCMessageType = 451 + ErrNeedMoreParams IRCMessageType = 461 + ErrAlreadyRegistered IRCMessageType = 462 + ErrNoPermForHost IRCMessageType = 463 + ErrPasswdMismatch IRCMessageType = 464 + ErrYoureBannedCreep IRCMessageType = 465 + ErrYouWillBeBanned IRCMessageType = 466 + ErrKeySet IRCMessageType = 467 + ErrChannelIsFull IRCMessageType = 471 + ErrUnknownMode IRCMessageType = 472 + ErrInviteOnlyChan IRCMessageType = 473 + ErrBannedFromChan IRCMessageType = 474 + ErrBadChannelKey IRCMessageType = 475 + ErrBadChanMask IRCMessageType = 476 + ErrNoChanModes IRCMessageType = 477 + ErrBanListFull IRCMessageType = 478 + ErrNoPrivileges IRCMessageType = 481 + ErrChanOpPrivsNeeded IRCMessageType = 482 + ErrCantKillServer IRCMessageType = 483 + ErrRestricted IRCMessageType = 484 + ErrUniqOpPrivsNeeded IRCMessageType = 485 + ErrNoOperHost IRCMessageType = 491 - ErrUmodeUnknownFlag MessageType = 501 - ErrUsersDoNotMatch MessageType = 502 + ErrUmodeUnknownFlag IRCMessageType = 501 + ErrUsersDoNotMatch IRCMessageType = 502 ) // names maps numeric codes to their standard IRC names. // //nolint:gochecknoglobals -var names = map[MessageType]string{ +var names = map[IRCMessageType]string{ RplWelcome: "RPL_WELCOME", RplYourHost: "RPL_YOURHOST", RplCreated: "RPL_CREATED", @@ -350,6 +384,8 @@ var names = map[MessageType]string{ // 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 MessageType) string { +// +// Deprecated: Use IRCMessageType.Name() instead. +func Name(code IRCMessageType) string { return names[code] }