Files

148 lines
4.3 KiB
Go
Raw Permalink Normal View History

package store
import (
"context"
"database/sql"
"time"
)
// SoundKind — назначение звука: саундборд или звук интерфейса (AGENT.md 7.13).
type SoundKind string
const (
SoundKindSoundboard SoundKind = "soundboard"
SoundKindUI SoundKind = "ui"
)
// GuildSound — звук сервера.
type GuildSound struct {
ID uint64
GuildID uint64
Name string
FileID uint64
Kind SoundKind
Event string
Emoji string
CreatorID *uint64
CreatedAt time.Time
}
// CreateGuildSoundParams — параметры нового звука.
type CreateGuildSoundParams struct {
ID uint64
GuildID uint64
Name string
FileID uint64
Kind SoundKind
Event string
Emoji string
CreatorID uint64
}
const guildSoundColumns = `id, guild_id, name, file_id, kind, event, emoji, creator_id, created_at`
// CreateGuildSound регистрирует звук сервера.
func (s *Store) CreateGuildSound(ctx context.Context, params CreateGuildSoundParams) (*GuildSound, error) {
if params.ID == 0 {
params.ID = s.NextID()
}
if params.Kind == "" {
params.Kind = SoundKindSoundboard
}
_, err := s.writer.ExecContext(ctx, `
INSERT INTO guild_sounds (id, guild_id, name, file_id, kind, event, emoji, creator_id, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
int64(params.ID), int64(params.GuildID), params.Name, int64(params.FileID),
string(params.Kind), params.Event, params.Emoji, int64(params.CreatorID), s.Now())
if err != nil {
return nil, mapError(err)
}
return s.GetGuildSound(ctx, params.ID)
}
func (s *Store) GetGuildSound(ctx context.Context, id uint64) (*GuildSound, error) {
row := s.reader.QueryRowContext(ctx, `SELECT `+guildSoundColumns+` FROM guild_sounds WHERE id = ?`, int64(id))
return scanGuildSound(row)
}
// ListGuildSounds отдаёт звуки сервера: all — оба вида, иначе только указанный.
func (s *Store) ListGuildSounds(ctx context.Context, guildID uint64, kind SoundKind, limit int) ([]GuildSound, error) {
if limit <= 0 || limit > 200 {
limit = 100
}
query := `SELECT ` + guildSoundColumns + ` FROM guild_sounds WHERE guild_id = ?`
args := []any{int64(guildID)}
if kind != "" {
query += ` AND kind = ?`
args = append(args, string(kind))
}
query += ` ORDER BY name LIMIT ?`
args = append(args, limit)
rows, err := s.reader.QueryContext(ctx, query, args...)
if err != nil {
return nil, err
}
defer rows.Close()
sounds := make([]GuildSound, 0, 16)
for rows.Next() {
sound, err := scanGuildSound(rows)
if err != nil {
return nil, err
}
sounds = append(sounds, *sound)
}
return sounds, rows.Err()
}
// CountGuildSounds считает звуки указанного вида (лимиты 30 + 30).
func (s *Store) CountGuildSounds(ctx context.Context, guildID uint64, kind SoundKind) (int, error) {
var count int
err := s.reader.QueryRowContext(ctx,
`SELECT COUNT(*) FROM guild_sounds WHERE guild_id = ? AND kind = ?`,
int64(guildID), string(kind)).Scan(&count)
return count, err
}
// RenameGuildSound меняет имя звука.
func (s *Store) RenameGuildSound(ctx context.Context, id uint64, name string, emoji string) (*GuildSound, error) {
result, err := s.writer.ExecContext(ctx,
`UPDATE guild_sounds SET name = ?, emoji = ? WHERE id = ?`, name, emoji, int64(id))
if err != nil {
return nil, mapError(err)
}
if affected, err := result.RowsAffected(); err == nil && affected == 0 {
return nil, ErrNotFound
}
return s.GetGuildSound(ctx, id)
}
// DeleteGuildSound удаляет звук сервера.
func (s *Store) DeleteGuildSound(ctx context.Context, id uint64) error {
result, err := s.writer.ExecContext(ctx, `DELETE FROM guild_sounds WHERE id = ?`, int64(id))
if err != nil {
return err
}
if affected, err := result.RowsAffected(); err == nil && affected == 0 {
return ErrNotFound
}
return nil
}
func scanGuildSound(scanner interface{ Scan(...any) error }) (*GuildSound, error) {
var (
sound GuildSound
creatorID sql.NullInt64
createdAt string
)
err := scanner.Scan(&sound.ID, &sound.GuildID, &sound.Name, &sound.FileID, &sound.Kind,
&sound.Event, &sound.Emoji, &creatorID, &createdAt)
if err != nil {
return nil, mapError(err)
}
sound.CreatorID = optionalID(creatorID)
sound.CreatedAt = parseTimestamp(createdAt)
return &sound, nil
}