Tickets: settings, category edit and ticket list from Discord; NOTIFY triggers in schema.sql
- /tickets settings (on or off, panel title and text, open tickets per member), /tickets category edit, /tickets list - schema.sql now creates the NOTIFY triggers, so the integration tests run on a database built from it, and two stale expectations (tickets start off) are fixed Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
This commit is contained in:
1 parent
d95ebf473e
commit
acdb5ead35
6 files changed
+483
-20
No files matched your search
@@ -7,6 +7,8 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -301,3 +303,110 @@ func (r *Repo) MarkCommandProcessed(ctx context.Context, id int64) error {
|
||||
_, err := r.db.ExecContext(ctx, `UPDATE ticket_commands SET processed_at = NOW() WHERE id = $1`, id)
|
||||
return err
|
||||
}
|
||||
|
||||
// SettingsPatch changes ticket settings. A nil field is left as it is.
|
||||
type SettingsPatch struct {
|
||||
Enabled *bool
|
||||
PanelTitle *string
|
||||
PanelDescription *string
|
||||
MaxOpenPerUser *int
|
||||
}
|
||||
|
||||
// UpdateSettings applies the fields that are set. The server gets a settings
|
||||
// row if it has none yet, with everything else at its default and the system
|
||||
// still off, until /tickets setup or this turns it on.
|
||||
func (r *Repo) UpdateSettings(ctx context.Context, guildID int64, p SettingsPatch) error {
|
||||
if _, err := r.db.ExecContext(ctx, `INSERT INTO ticket_settings (guild_id) VALUES ($1) ON CONFLICT DO NOTHING`, guildID); err != nil {
|
||||
return err
|
||||
}
|
||||
var sets []string
|
||||
args := []any{guildID}
|
||||
add := func(col string, v any) {
|
||||
args = append(args, v)
|
||||
sets = append(sets, col+` = $`+strconv.Itoa(len(args)))
|
||||
}
|
||||
if p.Enabled != nil {
|
||||
add("enabled", *p.Enabled)
|
||||
}
|
||||
if p.PanelTitle != nil {
|
||||
add("panel_title", *p.PanelTitle)
|
||||
}
|
||||
if p.PanelDescription != nil {
|
||||
add("panel_description", *p.PanelDescription)
|
||||
}
|
||||
if p.MaxOpenPerUser != nil {
|
||||
add("max_open_per_user", *p.MaxOpenPerUser)
|
||||
}
|
||||
if len(sets) == 0 {
|
||||
return nil
|
||||
}
|
||||
_, err := r.db.ExecContext(ctx, `UPDATE ticket_settings SET `+strings.Join(sets, ", ")+` WHERE guild_id = $1`, args...)
|
||||
return err
|
||||
}
|
||||
|
||||
// CategoryPatch changes a category. A nil field is left as it is; an empty
|
||||
// emoji or description clears it, and a zero StaffRoleID clears the role.
|
||||
type CategoryPatch struct {
|
||||
Name *string
|
||||
Emoji *string
|
||||
Description *string
|
||||
StaffRoleID *int64
|
||||
}
|
||||
|
||||
// UpdateCategory applies the fields that are set and returns the category as
|
||||
// saved, or nil when there is none with that ID in the guild.
|
||||
func (r *Repo) UpdateCategory(ctx context.Context, guildID, id int64, p CategoryPatch) (*Category, error) {
|
||||
var sets []string
|
||||
args := []any{guildID, id}
|
||||
add := func(col string, v any) {
|
||||
args = append(args, v)
|
||||
sets = append(sets, col+` = $`+strconv.Itoa(len(args)))
|
||||
}
|
||||
if p.Name != nil {
|
||||
add("name", *p.Name)
|
||||
}
|
||||
if p.Emoji != nil {
|
||||
add("emoji", nullText(*p.Emoji))
|
||||
}
|
||||
if p.Description != nil {
|
||||
add("description", *p.Description)
|
||||
}
|
||||
if p.StaffRoleID != nil {
|
||||
add("staff_role_id", nullID(*p.StaffRoleID))
|
||||
}
|
||||
if len(sets) == 0 {
|
||||
return r.GetCategory(ctx, guildID, id)
|
||||
}
|
||||
q := `UPDATE ticket_categories SET ` + strings.Join(sets, ", ") + ` WHERE guild_id = $1 AND id = $2 RETURNING ` + categoryCols
|
||||
c, err := scanCategory(r.db.QueryRowContext(ctx, q, args...))
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
// ListTickets returns a guild's most recent tickets with the given status
|
||||
// ("open" or "closed"), newest first.
|
||||
func (r *Repo) ListTickets(ctx context.Context, guildID int64, status string, limit int) ([]Ticket, error) {
|
||||
if limit < 1 || limit > 100 {
|
||||
limit = 25
|
||||
}
|
||||
q := `SELECT ` + ticketCols + ` FROM tickets WHERE guild_id = $1 AND status = $2 ORDER BY number DESC LIMIT $3`
|
||||
rows, err := r.db.QueryContext(ctx, q, guildID, status, limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var out []Ticket
|
||||
for rows.Next() {
|
||||
t, err := scanTicket(rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, t)
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
Reference in new issue
Block a user