Re-vendor the canonical files from sneak/prompts at dd4027b (closes #112)
check / check (push) Failing after 7s
check / check (push) Failing after 7s
The shared files are fetched from sneak/prompts dd4027b, with this repository's own entries after the shared content in .gitignore, .editorconfig and .dockerignore. Lint and tests are Dockerfile phases (golangci-lint v2.14.0, Debian Go 1.24.13) that the build stage depends on, and the Makefile targets call the script/ entrypoints. make fmt also formats Markdown with prettier. Fixes for the new lint findings: the auth cookie is always Secure, an IRC connection's relay goroutine stops when the connection closes, and repeated strings are constants. Model: opus-5-5
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
// Package db provides database access and migration management.
|
||||
package db
|
||||
|
||||
import (
|
||||
|
||||
+22
-12
@@ -1150,7 +1150,8 @@ func scanMessages(
|
||||
code, _ := strconv.Atoi(msg.Command)
|
||||
msg.Code = code
|
||||
|
||||
if mt, err := irc.FromInt(code); err == nil {
|
||||
mt, lookupErr := irc.FromInt(code)
|
||||
if lookupErr == nil {
|
||||
msg.Command = mt.Name()
|
||||
}
|
||||
}
|
||||
@@ -1373,7 +1374,9 @@ func (database *Database) GetStaleOrphanSessions(
|
||||
|
||||
for rows.Next() {
|
||||
var stale StaleSession
|
||||
if err := rows.Scan(&stale.ID, &stale.Nick); err != nil {
|
||||
|
||||
err = rows.Scan(&stale.ID, &stale.Nick)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"scan stale session: %w", err,
|
||||
)
|
||||
@@ -1382,7 +1385,8 @@ func (database *Database) GetStaleOrphanSessions(
|
||||
result = append(result, stale)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
err = rows.Err()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"iterate stale sessions: %w", err,
|
||||
)
|
||||
@@ -1905,9 +1909,10 @@ func (database *Database) ListChannelBans(
|
||||
for rows.Next() {
|
||||
var ban BanInfo
|
||||
|
||||
if scanErr := rows.Scan(
|
||||
scanErr := rows.Scan(
|
||||
&ban.Mask, &ban.SetBy, &ban.CreatedAt,
|
||||
); scanErr != nil {
|
||||
)
|
||||
if scanErr != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"scan channel ban: %w", scanErr,
|
||||
)
|
||||
@@ -1916,7 +1921,8 @@ func (database *Database) ListChannelBans(
|
||||
bans = append(bans, ban)
|
||||
}
|
||||
|
||||
if rowErr := rows.Err(); rowErr != nil {
|
||||
rowErr := rows.Err()
|
||||
if rowErr != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"iterate channel bans: %w", rowErr,
|
||||
)
|
||||
@@ -2247,11 +2253,12 @@ func (database *Database) ListAllChannelsWithCountsFiltered(
|
||||
for rows.Next() {
|
||||
var chanInfo ChannelInfoFull
|
||||
|
||||
if scanErr := rows.Scan(
|
||||
scanErr := rows.Scan(
|
||||
&chanInfo.Name,
|
||||
&chanInfo.MemberCount,
|
||||
&chanInfo.Topic,
|
||||
); scanErr != nil {
|
||||
)
|
||||
if scanErr != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"scan channel: %w", scanErr,
|
||||
)
|
||||
@@ -2260,7 +2267,8 @@ func (database *Database) ListAllChannelsWithCountsFiltered(
|
||||
channels = append(channels, chanInfo)
|
||||
}
|
||||
|
||||
if rowErr := rows.Err(); rowErr != nil {
|
||||
rowErr := rows.Err()
|
||||
if rowErr != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"iterate channels: %w", rowErr,
|
||||
)
|
||||
@@ -2308,11 +2316,12 @@ func (database *Database) GetSessionChannelsFiltered(
|
||||
for rows.Next() {
|
||||
var chanInfo ChannelInfo
|
||||
|
||||
if scanErr := rows.Scan(
|
||||
scanErr := rows.Scan(
|
||||
&chanInfo.ID,
|
||||
&chanInfo.Name,
|
||||
&chanInfo.Topic,
|
||||
); scanErr != nil {
|
||||
)
|
||||
if scanErr != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"scan channel: %w", scanErr,
|
||||
)
|
||||
@@ -2321,7 +2330,8 @@ func (database *Database) GetSessionChannelsFiltered(
|
||||
channels = append(channels, chanInfo)
|
||||
}
|
||||
|
||||
if rowErr := rows.Err(); rowErr != nil {
|
||||
rowErr := rows.Err()
|
||||
if rowErr != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"iterate channels: %w", rowErr,
|
||||
)
|
||||
|
||||
@@ -987,11 +987,12 @@ func TestGetOperCount(t *testing.T) {
|
||||
sid2, _, _, err := database.CreateSession(
|
||||
ctx, "user2", "", "", "",
|
||||
)
|
||||
_ = sid2
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_ = sid2
|
||||
|
||||
// Initially zero opers.
|
||||
count, err := database.GetOperCount(ctx)
|
||||
if err != nil {
|
||||
@@ -1023,23 +1024,25 @@ func TestGetOperCount(t *testing.T) {
|
||||
func TestWildcardMatch(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const hostmask = "nick!user@host"
|
||||
|
||||
tests := []struct {
|
||||
pattern string
|
||||
input string
|
||||
match bool
|
||||
}{
|
||||
{"*!*@*", "nick!user@host", true},
|
||||
{"*!*@*", hostmask, true},
|
||||
{"*!*@*.example.com", "nick!user@foo.example.com", true},
|
||||
{"*!*@*.example.com", "nick!user@other.net", false},
|
||||
{"badnick!*@*", "badnick!user@host", true},
|
||||
{"badnick!*@*", "goodnick!user@host", false},
|
||||
{"nick!user@host", "nick!user@host", true},
|
||||
{"nick!user@host", "nick!user@other", false},
|
||||
{hostmask, hostmask, true},
|
||||
{hostmask, "nick!user@other", false},
|
||||
{"*", "anything", true},
|
||||
{"?ick!*@*", "nick!user@host", true},
|
||||
{"?ick!*@*", hostmask, true},
|
||||
{"?ick!*@*", "nn!user@host", false},
|
||||
// Case-insensitive.
|
||||
{"Nick!*@*", "nick!user@host", true},
|
||||
{"Nick!*@*", hostmask, true},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
|
||||
+52
-60
@@ -125,21 +125,18 @@ func (hdlr *Handlers) authSession(
|
||||
}
|
||||
|
||||
// setAuthCookie sets the authentication cookie on the
|
||||
// response.
|
||||
// response. It is always Secure: the server runs behind a
|
||||
// TLS-terminating reverse proxy.
|
||||
func (hdlr *Handlers) setAuthCookie(
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
token string,
|
||||
) {
|
||||
secure := request.TLS != nil ||
|
||||
request.Header.Get("X-Forwarded-Proto") == "https"
|
||||
|
||||
http.SetCookie(writer, &http.Cookie{ //nolint:exhaustruct // optional fields
|
||||
Name: authCookieName,
|
||||
Value: token,
|
||||
Path: "/",
|
||||
HttpOnly: true,
|
||||
Secure: secure,
|
||||
Secure: true,
|
||||
SameSite: http.SameSiteStrictMode,
|
||||
})
|
||||
}
|
||||
@@ -148,17 +145,13 @@ func (hdlr *Handlers) setAuthCookie(
|
||||
// the client.
|
||||
func (hdlr *Handlers) clearAuthCookie(
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
) {
|
||||
secure := request.TLS != nil ||
|
||||
request.Header.Get("X-Forwarded-Proto") == "https"
|
||||
|
||||
http.SetCookie(writer, &http.Cookie{ //nolint:exhaustruct // optional fields
|
||||
Name: authCookieName,
|
||||
Value: "",
|
||||
Path: "/",
|
||||
HttpOnly: true,
|
||||
Secure: secure,
|
||||
Secure: true,
|
||||
SameSite: http.SameSiteStrictMode,
|
||||
MaxAge: -1,
|
||||
})
|
||||
@@ -172,7 +165,7 @@ func (hdlr *Handlers) requireAuth(
|
||||
hdlr.authSession(request)
|
||||
if err != nil {
|
||||
hdlr.respondJSON(writer, request, map[string]any{
|
||||
"error": "not registered",
|
||||
errorKey: "not registered",
|
||||
"numeric": irc.ErrNotRegistered,
|
||||
}, http.StatusUnauthorized)
|
||||
|
||||
@@ -285,11 +278,11 @@ func (hdlr *Handlers) executeCreateSession(
|
||||
|
||||
hdlr.deliverMOTD(request, clientID, sessionID, nick)
|
||||
|
||||
hdlr.setAuthCookie(writer, request, token)
|
||||
hdlr.setAuthCookie(writer, token)
|
||||
|
||||
hdlr.respondJSON(writer, request, map[string]any{
|
||||
"id": sessionID,
|
||||
"nick": nick,
|
||||
"id": sessionID,
|
||||
nickKey: nick,
|
||||
}, http.StatusCreated)
|
||||
}
|
||||
|
||||
@@ -643,7 +636,7 @@ func (hdlr *Handlers) HandleState() http.HandlerFunc {
|
||||
|
||||
hdlr.respondJSON(writer, request, map[string]any{
|
||||
"id": sessionID,
|
||||
"nick": nick,
|
||||
nickKey: nick,
|
||||
"channels": channels,
|
||||
}, http.StatusOK)
|
||||
}
|
||||
@@ -1090,7 +1083,7 @@ func (hdlr *Handlers) dispatchQueryCommand(
|
||||
)
|
||||
hdlr.broker.Notify(sessionID)
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "error"},
|
||||
map[string]string{statusKey: statusError},
|
||||
http.StatusOK)
|
||||
}
|
||||
}
|
||||
@@ -1112,7 +1105,7 @@ func (hdlr *Handlers) handlePrivmsg(
|
||||
)
|
||||
hdlr.broker.Notify(sessionID)
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "error"},
|
||||
map[string]string{statusKey: statusError},
|
||||
http.StatusOK)
|
||||
|
||||
return
|
||||
@@ -1127,7 +1120,7 @@ func (hdlr *Handlers) handlePrivmsg(
|
||||
)
|
||||
hdlr.broker.Notify(sessionID)
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "error"},
|
||||
map[string]string{statusKey: statusError},
|
||||
http.StatusOK)
|
||||
|
||||
return
|
||||
@@ -1169,7 +1162,7 @@ func (hdlr *Handlers) respondIRCError(
|
||||
)
|
||||
hdlr.broker.Notify(sessionID)
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "error"},
|
||||
map[string]string{statusKey: statusError},
|
||||
http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -1257,7 +1250,7 @@ func (hdlr *Handlers) handleChannelMsg(
|
||||
hdlr.broker.Notify(sessionID)
|
||||
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"id": uuid, "status": "sent"},
|
||||
map[string]string{"id": uuid, statusKey: "sent"},
|
||||
http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -1447,7 +1440,7 @@ func (hdlr *Handlers) handleDirectMsg(
|
||||
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{
|
||||
"id": result.UUID, "status": "sent",
|
||||
"id": result.UUID, statusKey: "sent",
|
||||
},
|
||||
http.StatusOK)
|
||||
}
|
||||
@@ -1522,7 +1515,7 @@ func (hdlr *Handlers) executeJoin(
|
||||
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{
|
||||
"status": "joined",
|
||||
statusKey: "joined",
|
||||
"channel": channel,
|
||||
},
|
||||
http.StatusOK)
|
||||
@@ -1679,6 +1672,7 @@ func (hdlr *Handlers) handlePart(
|
||||
|
||||
// Extract reason from body for the service call.
|
||||
reason := ""
|
||||
|
||||
if body != nil {
|
||||
var lines []string
|
||||
if json.Unmarshal(body, &lines) == nil &&
|
||||
@@ -1700,7 +1694,7 @@ func (hdlr *Handlers) handlePart(
|
||||
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{
|
||||
"status": "parted",
|
||||
statusKey: "parted",
|
||||
"channel": channel,
|
||||
},
|
||||
http.StatusOK)
|
||||
@@ -1739,7 +1733,7 @@ func (hdlr *Handlers) handleNick(
|
||||
if newNick == nick {
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{
|
||||
"status": "ok", "nick": newNick,
|
||||
statusKey: "ok", nickKey: newNick,
|
||||
},
|
||||
http.StatusOK)
|
||||
|
||||
@@ -1770,7 +1764,7 @@ func (hdlr *Handlers) executeNickChange(
|
||||
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{
|
||||
"status": "ok", "nick": newNick,
|
||||
statusKey: "ok", nickKey: newNick,
|
||||
},
|
||||
http.StatusOK)
|
||||
}
|
||||
@@ -1831,7 +1825,7 @@ func (hdlr *Handlers) handleTopic(
|
||||
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{
|
||||
"status": "ok", "topic": topic,
|
||||
statusKey: "ok", "topic": topic,
|
||||
},
|
||||
http.StatusOK)
|
||||
}
|
||||
@@ -1885,7 +1879,7 @@ func (hdlr *Handlers) dispatchInfoCommand(
|
||||
_ = target
|
||||
_ = bodyLines
|
||||
|
||||
okResp := map[string]string{"status": "ok"}
|
||||
okResp := map[string]string{statusKey: "ok"}
|
||||
|
||||
switch command {
|
||||
case irc.CmdMotd:
|
||||
@@ -1916,6 +1910,7 @@ func (hdlr *Handlers) handleQuit(
|
||||
body json.RawMessage,
|
||||
) {
|
||||
reason := "Client quit"
|
||||
|
||||
if body != nil {
|
||||
var lines []string
|
||||
if json.Unmarshal(body, &lines) == nil &&
|
||||
@@ -1928,10 +1923,10 @@ func (hdlr *Handlers) handleQuit(
|
||||
request.Context(), sessionID, nick, reason,
|
||||
)
|
||||
|
||||
hdlr.clearAuthCookie(writer, request)
|
||||
hdlr.clearAuthCookie(writer)
|
||||
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "quit"},
|
||||
map[string]string{statusKey: "quit"},
|
||||
http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -1963,7 +1958,7 @@ func (hdlr *Handlers) handleMode(
|
||||
)
|
||||
hdlr.broker.Notify(sessionID)
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "ok"},
|
||||
map[string]string{statusKey: "ok"},
|
||||
http.StatusOK)
|
||||
|
||||
return
|
||||
@@ -2064,7 +2059,7 @@ func (hdlr *Handlers) queryChannelMode(
|
||||
|
||||
hdlr.broker.Notify(sessionID)
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "ok"},
|
||||
map[string]string{statusKey: "ok"},
|
||||
http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -2241,7 +2236,7 @@ func (hdlr *Handlers) applyParameterizedMode(
|
||||
)
|
||||
hdlr.broker.Notify(sessionID)
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "error"},
|
||||
map[string]string{statusKey: statusError},
|
||||
http.StatusOK)
|
||||
}
|
||||
}
|
||||
@@ -2307,7 +2302,7 @@ func (hdlr *Handlers) applyUserMode(
|
||||
)
|
||||
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "ok"},
|
||||
map[string]string{statusKey: "ok"},
|
||||
http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -2353,7 +2348,7 @@ func (hdlr *Handlers) setChannelFlag(
|
||||
)
|
||||
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "ok"},
|
||||
map[string]string{statusKey: "ok"},
|
||||
http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -2437,7 +2432,7 @@ func (hdlr *Handlers) setHashcashMode(
|
||||
)
|
||||
hdlr.broker.Notify(sessionID)
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "ok"},
|
||||
map[string]string{statusKey: "ok"},
|
||||
http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -2486,7 +2481,7 @@ func (hdlr *Handlers) clearHashcashMode(
|
||||
)
|
||||
hdlr.broker.Notify(sessionID)
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "ok"},
|
||||
map[string]string{statusKey: "ok"},
|
||||
http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -2591,7 +2586,7 @@ func (hdlr *Handlers) executeBanChange(
|
||||
}
|
||||
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "ok"},
|
||||
map[string]string{statusKey: "ok"},
|
||||
http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -2645,7 +2640,7 @@ func (hdlr *Handlers) listBans(
|
||||
|
||||
hdlr.broker.Notify(sessionID)
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "ok"},
|
||||
map[string]string{statusKey: "ok"},
|
||||
http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -2709,7 +2704,7 @@ func (hdlr *Handlers) setChannelKeyMode(
|
||||
}
|
||||
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "ok"},
|
||||
map[string]string{statusKey: "ok"},
|
||||
http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -2758,7 +2753,7 @@ func (hdlr *Handlers) clearChannelKeyMode(
|
||||
}
|
||||
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "ok"},
|
||||
map[string]string{statusKey: "ok"},
|
||||
http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -2833,7 +2828,7 @@ func (hdlr *Handlers) setChannelLimitMode(
|
||||
}
|
||||
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "ok"},
|
||||
map[string]string{statusKey: "ok"},
|
||||
http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -2883,7 +2878,7 @@ func (hdlr *Handlers) clearChannelLimitMode(
|
||||
}
|
||||
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "ok"},
|
||||
map[string]string{statusKey: "ok"},
|
||||
http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -3050,7 +3045,7 @@ func (hdlr *Handlers) executeInvite(
|
||||
}
|
||||
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "ok"},
|
||||
map[string]string{statusKey: "ok"},
|
||||
http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -3119,7 +3114,7 @@ func (hdlr *Handlers) handleNames(
|
||||
|
||||
hdlr.broker.Notify(sessionID)
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "ok"},
|
||||
map[string]string{statusKey: "ok"},
|
||||
http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -3172,7 +3167,7 @@ func (hdlr *Handlers) handleList(
|
||||
|
||||
hdlr.broker.Notify(sessionID)
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "ok"},
|
||||
map[string]string{statusKey: "ok"},
|
||||
http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -3262,7 +3257,7 @@ func (hdlr *Handlers) executeWhois(
|
||||
|
||||
hdlr.broker.Notify(sessionID)
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "ok"},
|
||||
map[string]string{statusKey: "ok"},
|
||||
http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -3287,7 +3282,7 @@ func (hdlr *Handlers) whoisNotFound(
|
||||
)
|
||||
hdlr.broker.Notify(sessionID)
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "ok"},
|
||||
map[string]string{statusKey: "ok"},
|
||||
http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -3454,7 +3449,7 @@ func (hdlr *Handlers) handleWho(
|
||||
)
|
||||
hdlr.broker.Notify(sessionID)
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "ok"},
|
||||
map[string]string{statusKey: "ok"},
|
||||
http.StatusOK)
|
||||
|
||||
return
|
||||
@@ -3496,7 +3491,7 @@ func (hdlr *Handlers) handleWho(
|
||||
|
||||
hdlr.broker.Notify(sessionID)
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "ok"},
|
||||
map[string]string{statusKey: "ok"},
|
||||
http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -3512,7 +3507,7 @@ func (hdlr *Handlers) handleLusers(
|
||||
)
|
||||
hdlr.broker.Notify(sessionID)
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "ok"},
|
||||
map[string]string{statusKey: "ok"},
|
||||
http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -3703,10 +3698,10 @@ func (hdlr *Handlers) HandleLogout() http.HandlerFunc {
|
||||
)
|
||||
}
|
||||
|
||||
hdlr.clearAuthCookie(writer, request)
|
||||
hdlr.clearAuthCookie(writer)
|
||||
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "ok"},
|
||||
map[string]string{statusKey: "ok"},
|
||||
http.StatusOK)
|
||||
}
|
||||
}
|
||||
@@ -3809,7 +3804,7 @@ func (hdlr *Handlers) handleOper(
|
||||
|
||||
hdlr.broker.Notify(sessionID)
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "ok"},
|
||||
map[string]string{statusKey: "ok"},
|
||||
http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -3857,7 +3852,7 @@ func (hdlr *Handlers) handleAway(
|
||||
|
||||
hdlr.broker.Notify(sessionID)
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "ok"},
|
||||
map[string]string{statusKey: "ok"},
|
||||
http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -3919,7 +3914,7 @@ func (hdlr *Handlers) handleKick(
|
||||
}
|
||||
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "ok"},
|
||||
map[string]string{statusKey: "ok"},
|
||||
http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -3943,10 +3938,7 @@ func (hdlr *Handlers) deliverWhoisIdle(
|
||||
return
|
||||
}
|
||||
|
||||
idleSeconds := int64(time.Since(lastSeen).Seconds())
|
||||
if idleSeconds < 0 {
|
||||
idleSeconds = 0
|
||||
}
|
||||
idleSeconds := max(int64(time.Since(lastSeen).Seconds()), 0)
|
||||
|
||||
signonUnix := strconv.FormatInt(
|
||||
createdAt.Unix(), 10,
|
||||
|
||||
+321
-228
File diff suppressed because it is too large
Load Diff
@@ -117,11 +117,11 @@ func (hdlr *Handlers) executeLogin(
|
||||
request, clientID, sessionID, nick,
|
||||
)
|
||||
|
||||
hdlr.setAuthCookie(writer, request, token)
|
||||
hdlr.setAuthCookie(writer, token)
|
||||
|
||||
hdlr.respondJSON(writer, request, map[string]any{
|
||||
"id": sessionID,
|
||||
"nick": nick,
|
||||
"id": sessionID,
|
||||
nickKey: nick,
|
||||
}, http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -177,6 +177,6 @@ func (hdlr *Handlers) handlePass(
|
||||
}
|
||||
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{"status": "ok"},
|
||||
map[string]string{statusKey: "ok"},
|
||||
http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -24,6 +24,14 @@ import (
|
||||
|
||||
var errUnauthorized = errors.New("unauthorized")
|
||||
|
||||
// Field names and values used in many JSON responses.
|
||||
const (
|
||||
statusKey = "status"
|
||||
statusError = "error"
|
||||
errorKey = "error"
|
||||
nickKey = "nick"
|
||||
)
|
||||
|
||||
// Params defines the dependencies for creating Handlers.
|
||||
type Params struct {
|
||||
fx.In
|
||||
@@ -137,7 +145,7 @@ func (hdlr *Handlers) respondError(
|
||||
) {
|
||||
hdlr.respondJSON(
|
||||
writer, request,
|
||||
map[string]string{"error": msg},
|
||||
map[string]string{errorKey: msg},
|
||||
status,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -100,9 +100,10 @@ func (v *Validator) Validate(
|
||||
dateStr := parts[2]
|
||||
resource := parts[3]
|
||||
|
||||
if err := v.validateHeader(
|
||||
err := v.validateHeader(
|
||||
version, bitsStr, resource, requiredBits,
|
||||
); err != nil {
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -111,13 +112,13 @@ func (v *Validator) Validate(
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validateTime(stampTime); err != nil {
|
||||
err = validateTime(stampTime)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validateProof(
|
||||
stamp, requiredBits,
|
||||
); err != nil {
|
||||
err = validateProof(stamp, requiredBits)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -159,9 +159,7 @@ func (c *Conn) handleJoin(
|
||||
return
|
||||
}
|
||||
|
||||
channels := strings.Split(msg.Params[0], ",")
|
||||
|
||||
for _, chanName := range channels {
|
||||
for chanName := range strings.SplitSeq(msg.Params[0], ",") {
|
||||
chanName = strings.TrimSpace(chanName)
|
||||
|
||||
if !strings.HasPrefix(chanName, "#") {
|
||||
@@ -305,9 +303,7 @@ func (c *Conn) handlePart(
|
||||
reason = msg.Params[1]
|
||||
}
|
||||
|
||||
channels := strings.Split(msg.Params[0], ",")
|
||||
|
||||
for _, ch := range channels {
|
||||
for ch := range strings.SplitSeq(msg.Params[0], ",") {
|
||||
ch = strings.TrimSpace(ch)
|
||||
c.partChannel(ctx, ch, reason)
|
||||
}
|
||||
@@ -619,8 +615,8 @@ func (c *Conn) applyChannelModes(
|
||||
) {
|
||||
adding := true
|
||||
argIdx := 0
|
||||
applied := ""
|
||||
appliedArgs := ""
|
||||
|
||||
var applied, appliedArgs strings.Builder
|
||||
|
||||
for _, modeChar := range modeStr {
|
||||
var res modeResult
|
||||
@@ -672,16 +668,13 @@ func (c *Conn) applyChannelModes(
|
||||
argIdx += res.consumed
|
||||
|
||||
if !res.skip {
|
||||
applied += res.applied
|
||||
appliedArgs += res.appliedArgs
|
||||
applied.WriteString(res.applied)
|
||||
appliedArgs.WriteString(res.appliedArgs)
|
||||
}
|
||||
}
|
||||
|
||||
if applied != "" {
|
||||
modeReply := applied
|
||||
if appliedArgs != "" {
|
||||
modeReply += appliedArgs
|
||||
}
|
||||
if applied.Len() > 0 {
|
||||
modeReply := applied.String() + appliedArgs.String()
|
||||
|
||||
c.send(FormatMessage(
|
||||
c.hostmask(), "MODE", channel, modeReply,
|
||||
|
||||
@@ -62,7 +62,6 @@ type Conn struct {
|
||||
|
||||
lastQueueID int64
|
||||
closed bool
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
func newConn(
|
||||
@@ -151,8 +150,10 @@ func resolveHost(ctx context.Context, addr string) string {
|
||||
}
|
||||
|
||||
// serve is the main loop for a single IRC client connection.
|
||||
// Cancelling ctx when it returns stops the relay goroutine.
|
||||
func (c *Conn) serve(ctx context.Context) {
|
||||
ctx, c.cancel = context.WithCancel(ctx)
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
defer c.cleanup(ctx)
|
||||
|
||||
scanner := bufio.NewScanner(c.conn)
|
||||
@@ -481,7 +482,7 @@ func (c *Conn) deliverMOTD() {
|
||||
"- %s Message of the Day -", c.serverSfx,
|
||||
))
|
||||
|
||||
for _, line := range strings.Split(motd, "\n") {
|
||||
for line := range strings.SplitSeq(motd, "\n") {
|
||||
c.sendNumeric(irc.RplMotd, "- "+line)
|
||||
}
|
||||
|
||||
|
||||
@@ -282,6 +282,7 @@ func TestIntegrationTwoClients(t *testing.T) {
|
||||
|
||||
// Both nicks should appear in the name list.
|
||||
foundBothNames := false
|
||||
|
||||
for _, line := range aliceNames {
|
||||
if strings.Contains(line, " 353 ") &&
|
||||
strings.Contains(line, "alice") &&
|
||||
@@ -671,6 +672,7 @@ func TestIntegrationTwoClients(t *testing.T) {
|
||||
})
|
||||
|
||||
foundPartErr := false
|
||||
|
||||
for _, line := range bobPartFail {
|
||||
if strings.Contains(line, " 403 ") ||
|
||||
strings.Contains(line, " 442 ") {
|
||||
@@ -833,6 +835,7 @@ func TestIntegrationModeModerated(t *testing.T) {
|
||||
})
|
||||
|
||||
foundModErr := false
|
||||
|
||||
for _, line := range bobLines {
|
||||
if strings.Contains(line, " 404 ") ||
|
||||
strings.Contains(line, " 482 ") {
|
||||
@@ -859,6 +862,7 @@ func TestIntegrationModeModerated(t *testing.T) {
|
||||
})
|
||||
|
||||
bob.send("PRIVMSG #modtest :voiced message")
|
||||
|
||||
aliceLines := alice.readUntil(func(l string) bool {
|
||||
return strings.Contains(l, "voiced message")
|
||||
})
|
||||
|
||||
@@ -4,12 +4,18 @@ import (
|
||||
"testing"
|
||||
|
||||
"sneak.berlin/go/neoirc/internal/ircserver"
|
||||
"sneak.berlin/go/neoirc/pkg/irc"
|
||||
)
|
||||
|
||||
//nolint:funlen // table-driven test
|
||||
func TestParseMessage(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
nick = "alice"
|
||||
channel = "#general"
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
@@ -24,10 +30,10 @@ func TestParseMessage(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "simple command",
|
||||
input: "PING",
|
||||
input: irc.CmdPing,
|
||||
want: &ircserver.Message{
|
||||
Prefix: "",
|
||||
Command: "PING",
|
||||
Command: irc.CmdPing,
|
||||
Params: nil,
|
||||
},
|
||||
wantNil: false,
|
||||
@@ -38,7 +44,7 @@ func TestParseMessage(t *testing.T) {
|
||||
want: &ircserver.Message{
|
||||
Prefix: "",
|
||||
Command: "NICK",
|
||||
Params: []string{"alice"},
|
||||
Params: []string{nick},
|
||||
},
|
||||
wantNil: false,
|
||||
},
|
||||
@@ -57,8 +63,8 @@ func TestParseMessage(t *testing.T) {
|
||||
input: "PRIVMSG #general :hello world",
|
||||
want: &ircserver.Message{
|
||||
Prefix: "",
|
||||
Command: "PRIVMSG",
|
||||
Params: []string{"#general", "hello world"},
|
||||
Command: irc.CmdPrivmsg,
|
||||
Params: []string{channel, "hello world"},
|
||||
},
|
||||
wantNil: false,
|
||||
},
|
||||
@@ -68,7 +74,7 @@ func TestParseMessage(t *testing.T) {
|
||||
want: &ircserver.Message{
|
||||
Prefix: "server.example.com",
|
||||
Command: "001",
|
||||
Params: []string{"alice", "Welcome to IRC"},
|
||||
Params: []string{nick, "Welcome to IRC"},
|
||||
},
|
||||
wantNil: false,
|
||||
},
|
||||
@@ -79,7 +85,7 @@ func TestParseMessage(t *testing.T) {
|
||||
Prefix: "",
|
||||
Command: "USER",
|
||||
Params: []string{
|
||||
"alice", "0", "*", "Alice Smith",
|
||||
nick, "0", "*", "Alice Smith",
|
||||
},
|
||||
},
|
||||
wantNil: false,
|
||||
@@ -90,7 +96,7 @@ func TestParseMessage(t *testing.T) {
|
||||
want: &ircserver.Message{
|
||||
Prefix: "",
|
||||
Command: "JOIN",
|
||||
Params: []string{"#general"},
|
||||
Params: []string{channel},
|
||||
},
|
||||
wantNil: false,
|
||||
},
|
||||
@@ -99,17 +105,17 @@ func TestParseMessage(t *testing.T) {
|
||||
input: "QUIT :leaving now",
|
||||
want: &ircserver.Message{
|
||||
Prefix: "",
|
||||
Command: "QUIT",
|
||||
Command: irc.CmdQuit,
|
||||
Params: []string{"leaving now"},
|
||||
},
|
||||
wantNil: false,
|
||||
},
|
||||
{
|
||||
name: "quit without reason",
|
||||
input: "QUIT",
|
||||
input: irc.CmdQuit,
|
||||
want: &ircserver.Message{
|
||||
Prefix: "",
|
||||
Command: "QUIT",
|
||||
Command: irc.CmdQuit,
|
||||
Params: nil,
|
||||
},
|
||||
wantNil: false,
|
||||
@@ -120,7 +126,7 @@ func TestParseMessage(t *testing.T) {
|
||||
want: &ircserver.Message{
|
||||
Prefix: "",
|
||||
Command: "MODE",
|
||||
Params: []string{"#general"},
|
||||
Params: []string{channel},
|
||||
},
|
||||
wantNil: false,
|
||||
},
|
||||
@@ -131,7 +137,7 @@ func TestParseMessage(t *testing.T) {
|
||||
Prefix: "",
|
||||
Command: "KICK",
|
||||
Params: []string{
|
||||
"#general", "bob", "misbehaving",
|
||||
channel, "bob", "misbehaving",
|
||||
},
|
||||
},
|
||||
wantNil: false,
|
||||
@@ -141,8 +147,8 @@ func TestParseMessage(t *testing.T) {
|
||||
input: "PRIVMSG #general :",
|
||||
want: &ircserver.Message{
|
||||
Prefix: "",
|
||||
Command: "PRIVMSG",
|
||||
Params: []string{"#general", ""},
|
||||
Command: irc.CmdPrivmsg,
|
||||
Params: []string{channel, ""},
|
||||
},
|
||||
wantNil: false,
|
||||
},
|
||||
@@ -161,7 +167,7 @@ func TestParseMessage(t *testing.T) {
|
||||
input: "PING :irc.example.com",
|
||||
want: &ircserver.Message{
|
||||
Prefix: "",
|
||||
Command: "PING",
|
||||
Command: irc.CmdPing,
|
||||
Params: []string{"irc.example.com"},
|
||||
},
|
||||
wantNil: false,
|
||||
@@ -173,7 +179,7 @@ func TestParseMessage(t *testing.T) {
|
||||
Prefix: "",
|
||||
Command: "TOPIC",
|
||||
Params: []string{
|
||||
"#general",
|
||||
channel,
|
||||
"Welcome to the channel!",
|
||||
},
|
||||
},
|
||||
@@ -237,6 +243,12 @@ func TestParseMessage(t *testing.T) {
|
||||
func TestFormatMessage(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
nick = "alice"
|
||||
channel = "#general"
|
||||
serverName = "server"
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
prefix string
|
||||
@@ -247,35 +259,35 @@ func TestFormatMessage(t *testing.T) {
|
||||
{
|
||||
name: "simple command",
|
||||
prefix: "",
|
||||
command: "PING",
|
||||
command: irc.CmdPing,
|
||||
params: nil,
|
||||
want: "PING",
|
||||
want: irc.CmdPing,
|
||||
},
|
||||
{
|
||||
name: "with prefix",
|
||||
prefix: "server",
|
||||
prefix: serverName,
|
||||
command: "PONG",
|
||||
params: []string{"server"},
|
||||
params: []string{serverName},
|
||||
want: ":server PONG server",
|
||||
},
|
||||
{
|
||||
name: "privmsg with trailing",
|
||||
prefix: "alice!alice@host",
|
||||
command: "PRIVMSG",
|
||||
params: []string{"#general", "hello world"},
|
||||
command: irc.CmdPrivmsg,
|
||||
params: []string{channel, "hello world"},
|
||||
want: ":alice!alice@host PRIVMSG #general :hello world",
|
||||
},
|
||||
{
|
||||
name: "numeric reply",
|
||||
prefix: "server",
|
||||
prefix: serverName,
|
||||
command: "001",
|
||||
params: []string{"alice", "Welcome to IRC"},
|
||||
params: []string{nick, "Welcome to IRC"},
|
||||
want: ":server 001 alice :Welcome to IRC",
|
||||
},
|
||||
{
|
||||
name: "empty trailing",
|
||||
prefix: "server",
|
||||
command: "PRIVMSG",
|
||||
prefix: serverName,
|
||||
command: irc.CmdPrivmsg,
|
||||
params: []string{"#chan", ""},
|
||||
want: ":server PRIVMSG #chan :",
|
||||
},
|
||||
@@ -302,7 +314,7 @@ func TestParseFormatRoundTrip(t *testing.T) {
|
||||
// parameter either contains a space (gets ':' prefix
|
||||
// on format) or is a non-trailing single token.
|
||||
lines := []string{
|
||||
"PING",
|
||||
irc.CmdPing,
|
||||
"NICK alice",
|
||||
"PRIVMSG #general :hello world",
|
||||
"JOIN #general",
|
||||
|
||||
@@ -81,22 +81,24 @@ func New(
|
||||
// start begins listening for TCP connections.
|
||||
//
|
||||
//nolint:contextcheck // long-lived server ctx, not the short Fx one
|
||||
func (s *Server) start(_ context.Context, addr string) error {
|
||||
ln, err := net.Listen("tcp", addr)
|
||||
func (s *Server) start(ctx context.Context, addr string) error {
|
||||
var listenConfig net.ListenConfig
|
||||
|
||||
ln, err := listenConfig.Listen(ctx, "tcp", addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("irc listen: %w", err)
|
||||
}
|
||||
|
||||
s.listener = ln
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
serverCtx, cancel := context.WithCancel(context.Background())
|
||||
s.cancel = cancel
|
||||
|
||||
s.log.Info(
|
||||
"irc server listening", "addr", addr,
|
||||
)
|
||||
|
||||
go s.acceptLoop(ctx)
|
||||
go s.acceptLoop(serverCtx)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -71,7 +71,9 @@ func newTestEnv(t *testing.T) *testEnv {
|
||||
MOTD: "Welcome to test IRC",
|
||||
}
|
||||
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
var listenConfig net.ListenConfig
|
||||
|
||||
listener, err := listenConfig.Listen(t.Context(), "tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("listen: %v", err)
|
||||
}
|
||||
@@ -116,10 +118,12 @@ func newTestEnv(t *testing.T) *testEnv {
|
||||
func (env *testEnv) dial(t *testing.T) *testClient {
|
||||
t.Helper()
|
||||
|
||||
conn, err := net.DialTimeout(
|
||||
dialer := net.Dialer{Timeout: testTimeout}
|
||||
|
||||
conn, err := dialer.DialContext(
|
||||
t.Context(),
|
||||
"tcp",
|
||||
env.srv.Listener().Addr().String(),
|
||||
testTimeout,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("dial: %v", err)
|
||||
@@ -323,6 +327,7 @@ func TestPrivmsgBetweenClients(t *testing.T) {
|
||||
bob.joinAndDrain("#chat")
|
||||
|
||||
alice.send("PRIVMSG #chat :hello bob!")
|
||||
|
||||
lines := bob.sendAndExpect("PING :sync", "hello bob!")
|
||||
assertContains(t, lines, "hello bob!", "channel PRIVMSG")
|
||||
}
|
||||
|
||||
@@ -78,7 +78,9 @@ func New(
|
||||
srv.enableSentry()
|
||||
srv.SetupRoutes()
|
||||
|
||||
go srv.serve() //nolint:contextcheck
|
||||
// The start hook's context ends when the hook
|
||||
// returns; serving must outlive it.
|
||||
go srv.serve() //nolint:contextcheck,gosec // G118
|
||||
|
||||
return nil
|
||||
},
|
||||
|
||||
+29
-28
@@ -19,6 +19,12 @@ import (
|
||||
"sneak.berlin/go/neoirc/pkg/irc"
|
||||
)
|
||||
|
||||
// Error texts that several commands reply with.
|
||||
const (
|
||||
msgNoSuchChannel = "No such channel"
|
||||
msgNotChannelOp = "You're not channel operator"
|
||||
)
|
||||
|
||||
// Params defines the dependencies for creating a Service.
|
||||
type Params struct {
|
||||
fx.In
|
||||
@@ -142,7 +148,7 @@ func (s *Service) SendChannelMessage(
|
||||
return 0, "", &IRCError{
|
||||
irc.ErrNoSuchChannel,
|
||||
[]string{channel},
|
||||
"No such channel",
|
||||
msgNoSuchChannel,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,10 +262,11 @@ func (s *Service) JoinChannel(
|
||||
isCreator := countErr == nil && memberCount == 0
|
||||
|
||||
if !isCreator {
|
||||
if joinErr := checkJoinRestrictions(
|
||||
joinErr := checkJoinRestrictions(
|
||||
ctx, s.db, chID, sessionID,
|
||||
channel, suppliedKey, memberCount,
|
||||
); joinErr != nil {
|
||||
)
|
||||
if joinErr != nil {
|
||||
return nil, joinErr
|
||||
}
|
||||
}
|
||||
@@ -306,7 +313,7 @@ func (s *Service) PartChannel(
|
||||
return &IRCError{
|
||||
irc.ErrNoSuchChannel,
|
||||
[]string{channel},
|
||||
"No such channel",
|
||||
msgNoSuchChannel,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,7 +355,7 @@ func (s *Service) SetTopic(
|
||||
return &IRCError{
|
||||
irc.ErrNoSuchChannel,
|
||||
[]string{channel},
|
||||
"No such channel",
|
||||
msgNoSuchChannel,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,14 +379,13 @@ func (s *Service) SetTopic(
|
||||
return &IRCError{
|
||||
irc.ErrChanOpPrivsNeeded,
|
||||
[]string{channel},
|
||||
"You're not channel operator",
|
||||
msgNotChannelOp,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if setErr := s.db.SetTopic(
|
||||
ctx, channel, topic,
|
||||
); setErr != nil {
|
||||
setErr := s.db.SetTopic(ctx, channel, topic)
|
||||
if setErr != nil {
|
||||
return fmt.Errorf("set topic: %w", setErr)
|
||||
}
|
||||
|
||||
@@ -409,7 +415,7 @@ func (s *Service) KickUser(
|
||||
return &IRCError{
|
||||
irc.ErrNoSuchChannel,
|
||||
[]string{channel},
|
||||
"No such channel",
|
||||
msgNoSuchChannel,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,7 +426,7 @@ func (s *Service) KickUser(
|
||||
return &IRCError{
|
||||
irc.ErrChanOpPrivsNeeded,
|
||||
[]string{channel},
|
||||
"You're not channel operator",
|
||||
msgNotChannelOp,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -609,7 +615,7 @@ func (s *Service) ValidateChannelOp(
|
||||
return 0, &IRCError{
|
||||
irc.ErrNoSuchChannel,
|
||||
[]string{channel},
|
||||
"No such channel",
|
||||
msgNoSuchChannel,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -620,7 +626,7 @@ func (s *Service) ValidateChannelOp(
|
||||
return 0, &IRCError{
|
||||
irc.ErrChanOpPrivsNeeded,
|
||||
[]string{channel},
|
||||
"You're not channel operator",
|
||||
msgNotChannelOp,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -682,33 +688,28 @@ func (s *Service) SetChannelFlag(
|
||||
) error {
|
||||
switch flag {
|
||||
case 'm':
|
||||
if err := s.db.SetChannelModerated(
|
||||
ctx, chID, setting,
|
||||
); err != nil {
|
||||
err := s.db.SetChannelModerated(ctx, chID, setting)
|
||||
if err != nil {
|
||||
return fmt.Errorf("set moderated: %w", err)
|
||||
}
|
||||
case 't':
|
||||
if err := s.db.SetChannelTopicLocked(
|
||||
ctx, chID, setting,
|
||||
); err != nil {
|
||||
err := s.db.SetChannelTopicLocked(ctx, chID, setting)
|
||||
if err != nil {
|
||||
return fmt.Errorf("set topic locked: %w", err)
|
||||
}
|
||||
case 'i':
|
||||
if err := s.db.SetChannelInviteOnly(
|
||||
ctx, chID, setting,
|
||||
); err != nil {
|
||||
err := s.db.SetChannelInviteOnly(ctx, chID, setting)
|
||||
if err != nil {
|
||||
return fmt.Errorf("set invite only: %w", err)
|
||||
}
|
||||
case 's':
|
||||
if err := s.db.SetChannelSecret(
|
||||
ctx, chID, setting,
|
||||
); err != nil {
|
||||
err := s.db.SetChannelSecret(ctx, chID, setting)
|
||||
if err != nil {
|
||||
return fmt.Errorf("set secret: %w", err)
|
||||
}
|
||||
case 'n':
|
||||
if err := s.db.SetChannelNoExternal(
|
||||
ctx, chID, setting,
|
||||
); err != nil {
|
||||
err := s.db.SetChannelNoExternal(ctx, chID, setting)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"set no external: %w", err,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user