diff --git a/soundboard/assets/soundboard.html b/soundboard/assets/soundboard.html index fd81052f3e..4e2991c845 100644 --- a/soundboard/assets/soundboard.html +++ b/soundboard/assets/soundboard.html @@ -48,7 +48,7 @@

Existing sounds

{{$dot := .}} - {{range .Config.Sounds}} + {{range .SoundboardSounds}}
{{end}} @@ -64,7 +64,7 @@

Existing sounds

{{$guildmember := .ActiveGuild.ID}} {{$roles := .ActiveGuild.Roles}} {{$highest := .HighestRole}} - {{range .Config.Sounds}} + {{range .SoundboardSounds}}
@@ -77,7 +77,7 @@

Existing sounds

-

{{.Status}}

+

{{if eq .Status 0}}Queued{{else if eq .Status 1}}Ready{{else if eq .Status 2}}Processing{{else if eq .Status 3}}Too long{{else if eq .Status 4}}Failed, contact support{{end}}

diff --git a/soundboard/bot.go b/soundboard/bot.go index 46b274ebdd..c0212eea7f 100644 --- a/soundboard/bot.go +++ b/soundboard/bot.go @@ -3,11 +3,9 @@ package soundboard import ( "github.com/jonas747/dcmd" "github.com/jonas747/dstate" - "github.com/jonas747/yagpdb/bot" "github.com/jonas747/yagpdb/commands" - "github.com/jonas747/yagpdb/common/configstore" + "github.com/jonas747/yagpdb/soundboard/models" "github.com/pkg/errors" - "golang.org/x/net/context" "strings" ) @@ -21,33 +19,30 @@ func (p *Plugin) AddCommands() { &dcmd.ArgDef{Name: "Name", Type: dcmd.String}, }, RunFunc: func(data *dcmd.Data) (interface{}, error) { - config := &SoundboardConfig{} - err := configstore.Cached.GetGuildConfig(context.Background(), data.GS.ID, config) + sounds, err := GetSoundboardSounds(data.GS.ID, data.Context()) if err != nil { - return nil, errors.WithMessage(err, "GetGuildConfig") + return nil, errors.WithMessage(err, "GetSoundboardSounds") } // Get member from api or state - member, err := bot.GetMember(data.GS.ID, data.Msg.Author.ID) - if err != nil { - return nil, errors.WithMessage(err, "GetMember") - } + member := commands.ContextMS(data.Context()) if data.Args[0].Str() == "" { - return ListSounds(config, member), nil + return ListSounds(sounds, member), nil } - var sound *SoundboardSound - for _, v := range config.Sounds { - if strings.ToLower(v.Name) == strings.ToLower(data.Args[0].Str()) { + var sound *models.SoundboardSound + for _, v := range sounds { + if strings.EqualFold(v.Name, data.Args[0].Str()) { sound = v break } } + if sound == nil { - return "Sound not found, " + ListSounds(config, member), nil - } else if !sound.CanPlay(member.Roles) { - return "You can't play that sound, " + ListSounds(config, member), nil + return "Sound not found, " + ListSounds(sounds, member), nil + } else if !CanPlaySound(sound, member.Roles) { + return "You can't play that sound, " + ListSounds(sounds, member), nil } data.GS.RLock() @@ -72,12 +67,12 @@ func (p *Plugin) AddCommands() { }) } -func ListSounds(config *SoundboardConfig, ms *dstate.MemberState) string { +func ListSounds(sounds []*models.SoundboardSound, ms *dstate.MemberState) string { canPlay := "" restricted := "" - for _, sound := range config.Sounds { - if sound.CanPlay(ms.Roles) { + for _, sound := range sounds { + if CanPlaySound(sound, ms.Roles) { canPlay += "`" + sound.Name + "`, " } else { restricted += "`" + sound.Name + "`, " diff --git a/soundboard/models/boil_main_test.go b/soundboard/models/boil_main_test.go new file mode 100644 index 0000000000..50bfc5061f --- /dev/null +++ b/soundboard/models/boil_main_test.go @@ -0,0 +1,119 @@ +// Code generated by SQLBoiler (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. +// This file is meant to be re-generated in place and/or deleted at any time. + +package models + +import ( + "database/sql" + "flag" + "fmt" + "math/rand" + "os" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/spf13/viper" + "github.com/volatiletech/sqlboiler/boil" +) + +var flagDebugMode = flag.Bool("test.sqldebug", false, "Turns on debug mode for SQL statements") +var flagConfigFile = flag.String("test.config", "", "Overrides the default config") + +const outputDirDepth = 1 + +var ( + dbMain tester +) + +type tester interface { + setup() error + conn() (*sql.DB, error) + teardown() error +} + +func TestMain(m *testing.M) { + if dbMain == nil { + fmt.Println("no dbMain tester interface was ready") + os.Exit(-1) + } + + rand.Seed(time.Now().UnixNano()) + + flag.Parse() + + var err error + + // Load configuration + err = initViper() + if err != nil { + fmt.Println("unable to load config file") + os.Exit(-2) + } + + // Set DebugMode so we can see generated sql statements + boil.DebugMode = *flagDebugMode + + if err = dbMain.setup(); err != nil { + fmt.Println("Unable to execute setup:", err) + os.Exit(-4) + } + + conn, err := dbMain.conn() + if err != nil { + fmt.Println("failed to get connection:", err) + } + + var code int + boil.SetDB(conn) + code = m.Run() + + if err = dbMain.teardown(); err != nil { + fmt.Println("Unable to execute teardown:", err) + os.Exit(-5) + } + + os.Exit(code) +} + +func initViper() error { + if flagConfigFile != nil && *flagConfigFile != "" { + viper.SetConfigFile(*flagConfigFile) + if err := viper.ReadInConfig(); err != nil { + return err + } + return nil + } + + var err error + + viper.SetConfigName("sqlboiler") + + configHome := os.Getenv("XDG_CONFIG_HOME") + homePath := os.Getenv("HOME") + wd, err := os.Getwd() + if err != nil { + wd = strings.Repeat("../", outputDirDepth) + } else { + wd = wd + strings.Repeat("/..", outputDirDepth) + } + + configPaths := []string{wd} + if len(configHome) > 0 { + configPaths = append(configPaths, filepath.Join(configHome, "sqlboiler")) + } else { + configPaths = append(configPaths, filepath.Join(homePath, ".config/sqlboiler")) + } + + for _, p := range configPaths { + viper.AddConfigPath(p) + } + + // Ignore errors here, fall back to defaults and validation to provide errs + _ = viper.ReadInConfig() + viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) + viper.AutomaticEnv() + + return nil +} diff --git a/soundboard/models/boil_queries.go b/soundboard/models/boil_queries.go new file mode 100644 index 0000000000..edf30aef6e --- /dev/null +++ b/soundboard/models/boil_queries.go @@ -0,0 +1,33 @@ +// Code generated by SQLBoiler (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. +// This file is meant to be re-generated in place and/or deleted at any time. + +package models + +import ( + "github.com/volatiletech/sqlboiler/drivers" + "github.com/volatiletech/sqlboiler/queries" + "github.com/volatiletech/sqlboiler/queries/qm" +) + +var dialect = drivers.Dialect{ + LQ: 0x22, + RQ: 0x22, + + UseIndexPlaceholders: true, + UseLastInsertID: false, + UseSchema: false, + UseDefaultKeyword: true, + UseAutoColumns: false, + UseTopClause: false, + UseOutputClause: false, + UseCaseWhenExistsClause: false, +} + +// NewQuery initializes a new Query using the passed in QueryMods +func NewQuery(mods ...qm.QueryMod) *queries.Query { + q := &queries.Query{} + queries.SetDialect(q, &dialect) + qm.Apply(q, mods...) + + return q +} diff --git a/soundboard/models/boil_queries_test.go b/soundboard/models/boil_queries_test.go new file mode 100644 index 0000000000..dfa4393ec9 --- /dev/null +++ b/soundboard/models/boil_queries_test.go @@ -0,0 +1,52 @@ +// Code generated by SQLBoiler (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. +// This file is meant to be re-generated in place and/or deleted at any time. + +package models + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "math/rand" + "regexp" + + "github.com/volatiletech/sqlboiler/boil" +) + +var dbNameRand *rand.Rand + +func MustTx(transactor boil.ContextTransactor, err error) boil.ContextTransactor { + if err != nil { + panic(fmt.Sprintf("Cannot create a transactor: %s", err)) + } + return transactor +} + +func newFKeyDestroyer(regex *regexp.Regexp, reader io.Reader) io.Reader { + return &fKeyDestroyer{ + reader: reader, + rgx: regex, + } +} + +type fKeyDestroyer struct { + reader io.Reader + buf *bytes.Buffer + rgx *regexp.Regexp +} + +func (f *fKeyDestroyer) Read(b []byte) (int, error) { + if f.buf == nil { + all, err := ioutil.ReadAll(f.reader) + if err != nil { + return 0, err + } + + all = bytes.Replace(all, []byte{'\r', '\n'}, []byte{'\n'}, -1) + all = f.rgx.ReplaceAll(all, []byte{}) + f.buf = bytes.NewBuffer(all) + } + + return f.buf.Read(b) +} diff --git a/soundboard/models/boil_suites_test.go b/soundboard/models/boil_suites_test.go new file mode 100644 index 0000000000..47c33ee17f --- /dev/null +++ b/soundboard/models/boil_suites_test.go @@ -0,0 +1,117 @@ +// Code generated by SQLBoiler (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. +// This file is meant to be re-generated in place and/or deleted at any time. + +package models + +import "testing" + +// This test suite runs each operation test in parallel. +// Example, if your database has 3 tables, the suite will run: +// table1, table2 and table3 Delete in parallel +// table1, table2 and table3 Insert in parallel, and so forth. +// It does NOT run each operation group in parallel. +// Separating the tests thusly grants avoidance of Postgres deadlocks. +func TestParent(t *testing.T) { + t.Run("SoundboardSounds", testSoundboardSounds) +} + +func TestDelete(t *testing.T) { + t.Run("SoundboardSounds", testSoundboardSoundsDelete) +} + +func TestQueryDeleteAll(t *testing.T) { + t.Run("SoundboardSounds", testSoundboardSoundsQueryDeleteAll) +} + +func TestSliceDeleteAll(t *testing.T) { + t.Run("SoundboardSounds", testSoundboardSoundsSliceDeleteAll) +} + +func TestExists(t *testing.T) { + t.Run("SoundboardSounds", testSoundboardSoundsExists) +} + +func TestFind(t *testing.T) { + t.Run("SoundboardSounds", testSoundboardSoundsFind) +} + +func TestBind(t *testing.T) { + t.Run("SoundboardSounds", testSoundboardSoundsBind) +} + +func TestOne(t *testing.T) { + t.Run("SoundboardSounds", testSoundboardSoundsOne) +} + +func TestAll(t *testing.T) { + t.Run("SoundboardSounds", testSoundboardSoundsAll) +} + +func TestCount(t *testing.T) { + t.Run("SoundboardSounds", testSoundboardSoundsCount) +} + +func TestInsert(t *testing.T) { + t.Run("SoundboardSounds", testSoundboardSoundsInsert) + t.Run("SoundboardSounds", testSoundboardSoundsInsertWhitelist) +} + +// TestToOne tests cannot be run in parallel +// or deadlocks can occur. +func TestToOne(t *testing.T) {} + +// TestOneToOne tests cannot be run in parallel +// or deadlocks can occur. +func TestOneToOne(t *testing.T) {} + +// TestToMany tests cannot be run in parallel +// or deadlocks can occur. +func TestToMany(t *testing.T) {} + +// TestToOneSet tests cannot be run in parallel +// or deadlocks can occur. +func TestToOneSet(t *testing.T) {} + +// TestToOneRemove tests cannot be run in parallel +// or deadlocks can occur. +func TestToOneRemove(t *testing.T) {} + +// TestOneToOneSet tests cannot be run in parallel +// or deadlocks can occur. +func TestOneToOneSet(t *testing.T) {} + +// TestOneToOneRemove tests cannot be run in parallel +// or deadlocks can occur. +func TestOneToOneRemove(t *testing.T) {} + +// TestToManyAdd tests cannot be run in parallel +// or deadlocks can occur. +func TestToManyAdd(t *testing.T) {} + +// TestToManySet tests cannot be run in parallel +// or deadlocks can occur. +func TestToManySet(t *testing.T) {} + +// TestToManyRemove tests cannot be run in parallel +// or deadlocks can occur. +func TestToManyRemove(t *testing.T) {} + +func TestReload(t *testing.T) { + t.Run("SoundboardSounds", testSoundboardSoundsReload) +} + +func TestReloadAll(t *testing.T) { + t.Run("SoundboardSounds", testSoundboardSoundsReloadAll) +} + +func TestSelect(t *testing.T) { + t.Run("SoundboardSounds", testSoundboardSoundsSelect) +} + +func TestUpdate(t *testing.T) { + t.Run("SoundboardSounds", testSoundboardSoundsUpdate) +} + +func TestSliceUpdateAll(t *testing.T) { + t.Run("SoundboardSounds", testSoundboardSoundsSliceUpdateAll) +} diff --git a/soundboard/models/boil_table_names.go b/soundboard/models/boil_table_names.go new file mode 100644 index 0000000000..18c5c40cb1 --- /dev/null +++ b/soundboard/models/boil_table_names.go @@ -0,0 +1,10 @@ +// Code generated by SQLBoiler (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. +// This file is meant to be re-generated in place and/or deleted at any time. + +package models + +var TableNames = struct { + SoundboardSounds string +}{ + SoundboardSounds: "soundboard_sounds", +} diff --git a/soundboard/models/boil_types.go b/soundboard/models/boil_types.go new file mode 100644 index 0000000000..7f766dec6c --- /dev/null +++ b/soundboard/models/boil_types.go @@ -0,0 +1,52 @@ +// Code generated by SQLBoiler (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. +// This file is meant to be re-generated in place and/or deleted at any time. + +package models + +import ( + "strconv" + + "github.com/pkg/errors" + "github.com/volatiletech/sqlboiler/boil" + "github.com/volatiletech/sqlboiler/strmangle" +) + +// M type is for providing columns and column values to UpdateAll. +type M map[string]interface{} + +// ErrSyncFail occurs during insert when the record could not be retrieved in +// order to populate default value information. This usually happens when LastInsertId +// fails or there was a primary key configuration that was not resolvable. +var ErrSyncFail = errors.New("models: failed to synchronize data after insert") + +type insertCache struct { + query string + retQuery string + valueMapping []uint64 + retMapping []uint64 +} + +type updateCache struct { + query string + valueMapping []uint64 +} + +func makeCacheKey(cols boil.Columns, nzDefaults []string) string { + buf := strmangle.GetBuffer() + + buf.WriteString(strconv.Itoa(cols.Kind)) + for _, w := range cols.Cols { + buf.WriteString(w) + } + + if len(nzDefaults) != 0 { + buf.WriteByte('.') + } + for _, nz := range nzDefaults { + buf.WriteString(nz) + } + + str := buf.String() + strmangle.PutBuffer(buf) + return str +} diff --git a/soundboard/models/psql_main_test.go b/soundboard/models/psql_main_test.go new file mode 100644 index 0000000000..05c42a1c22 --- /dev/null +++ b/soundboard/models/psql_main_test.go @@ -0,0 +1,214 @@ +// Code generated by SQLBoiler (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. +// This file is meant to be re-generated in place and/or deleted at any time. + +package models + +import ( + "bytes" + "database/sql" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "regexp" + "strings" + + "github.com/kat-co/vala" + _ "github.com/lib/pq" + "github.com/pkg/errors" + "github.com/spf13/viper" + "github.com/volatiletech/sqlboiler/drivers/sqlboiler-psql/driver" + "github.com/volatiletech/sqlboiler/randomize" +) + +var rgxPGFkey = regexp.MustCompile(`(?m)^ALTER TABLE ONLY .*\n\s+ADD CONSTRAINT .*? FOREIGN KEY .*?;\n`) + +type pgTester struct { + dbConn *sql.DB + + dbName string + host string + user string + pass string + sslmode string + port int + + pgPassFile string + + testDBName string +} + +func init() { + dbMain = &pgTester{} +} + +// setup dumps the database schema and imports it into a temporary randomly +// generated test database so that tests can be run against it using the +// generated sqlboiler ORM package. +func (p *pgTester) setup() error { + var err error + + viper.SetDefault("psql.schema", "public") + viper.SetDefault("psql.port", 5432) + viper.SetDefault("psql.sslmode", "require") + + p.dbName = viper.GetString("psql.dbname") + p.host = viper.GetString("psql.host") + p.user = viper.GetString("psql.user") + p.pass = viper.GetString("psql.pass") + p.port = viper.GetInt("psql.port") + p.sslmode = viper.GetString("psql.sslmode") + + err = vala.BeginValidation().Validate( + vala.StringNotEmpty(p.user, "psql.user"), + vala.StringNotEmpty(p.host, "psql.host"), + vala.Not(vala.Equals(p.port, 0, "psql.port")), + vala.StringNotEmpty(p.dbName, "psql.dbname"), + vala.StringNotEmpty(p.sslmode, "psql.sslmode"), + ).Check() + + if err != nil { + return err + } + + // Create a randomized db name. + p.testDBName = randomize.StableDBName(p.dbName) + + if err = p.makePGPassFile(); err != nil { + return err + } + + if err = p.dropTestDB(); err != nil { + return err + } + if err = p.createTestDB(); err != nil { + return err + } + + dumpCmd := exec.Command("pg_dump", "--schema-only", p.dbName) + dumpCmd.Env = append(os.Environ(), p.pgEnv()...) + createCmd := exec.Command("psql", p.testDBName) + createCmd.Env = append(os.Environ(), p.pgEnv()...) + + r, w := io.Pipe() + dumpCmd.Stdout = w + createCmd.Stdin = newFKeyDestroyer(rgxPGFkey, r) + + if err = dumpCmd.Start(); err != nil { + return errors.Wrap(err, "failed to start pg_dump command") + } + if err = createCmd.Start(); err != nil { + return errors.Wrap(err, "failed to start psql command") + } + + if err = dumpCmd.Wait(); err != nil { + fmt.Println(err) + return errors.Wrap(err, "failed to wait for pg_dump command") + } + + _ = w.Close() // After dumpCmd is done, close the write end of the pipe + + if err = createCmd.Wait(); err != nil { + fmt.Println(err) + return errors.Wrap(err, "failed to wait for psql command") + } + + return nil +} + +func (p *pgTester) runCmd(stdin, command string, args ...string) error { + cmd := exec.Command(command, args...) + cmd.Env = append(os.Environ(), p.pgEnv()...) + + if len(stdin) != 0 { + cmd.Stdin = strings.NewReader(stdin) + } + + stdout := &bytes.Buffer{} + stderr := &bytes.Buffer{} + cmd.Stdout = stdout + cmd.Stderr = stderr + if err := cmd.Run(); err != nil { + fmt.Println("failed running:", command, args) + fmt.Println(stdout.String()) + fmt.Println(stderr.String()) + return err + } + + return nil +} + +func (p *pgTester) pgEnv() []string { + return []string{ + fmt.Sprintf("PGHOST=%s", p.host), + fmt.Sprintf("PGPORT=%d", p.port), + fmt.Sprintf("PGUSER=%s", p.user), + fmt.Sprintf("PGPASSFILE=%s", p.pgPassFile), + } +} + +func (p *pgTester) makePGPassFile() error { + tmp, err := ioutil.TempFile("", "pgpass") + if err != nil { + return errors.Wrap(err, "failed to create option file") + } + + fmt.Fprintf(tmp, "%s:%d:postgres:%s", p.host, p.port, p.user) + if len(p.pass) != 0 { + fmt.Fprintf(tmp, ":%s", p.pass) + } + fmt.Fprintln(tmp) + + fmt.Fprintf(tmp, "%s:%d:%s:%s", p.host, p.port, p.dbName, p.user) + if len(p.pass) != 0 { + fmt.Fprintf(tmp, ":%s", p.pass) + } + fmt.Fprintln(tmp) + + fmt.Fprintf(tmp, "%s:%d:%s:%s", p.host, p.port, p.testDBName, p.user) + if len(p.pass) != 0 { + fmt.Fprintf(tmp, ":%s", p.pass) + } + fmt.Fprintln(tmp) + + p.pgPassFile = tmp.Name() + return tmp.Close() +} + +func (p *pgTester) createTestDB() error { + return p.runCmd("", "createdb", p.testDBName) +} + +func (p *pgTester) dropTestDB() error { + return p.runCmd("", "dropdb", "--if-exists", p.testDBName) +} + +// teardown executes cleanup tasks when the tests finish running +func (p *pgTester) teardown() error { + var err error + if err = p.dbConn.Close(); err != nil { + return err + } + p.dbConn = nil + + if err = p.dropTestDB(); err != nil { + return err + } + + return os.Remove(p.pgPassFile) +} + +func (p *pgTester) conn() (*sql.DB, error) { + if p.dbConn != nil { + return p.dbConn, nil + } + + var err error + p.dbConn, err = sql.Open("postgres", driver.PSQLBuildQueryString(p.user, p.pass, p.testDBName, p.host, p.port, p.sslmode)) + if err != nil { + return nil, err + } + + return p.dbConn, nil +} diff --git a/soundboard/models/psql_suites_test.go b/soundboard/models/psql_suites_test.go new file mode 100644 index 0000000000..fbf3bcd3ab --- /dev/null +++ b/soundboard/models/psql_suites_test.go @@ -0,0 +1,10 @@ +// Code generated by SQLBoiler (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. +// This file is meant to be re-generated in place and/or deleted at any time. + +package models + +import "testing" + +func TestUpsert(t *testing.T) { + t.Run("SoundboardSounds", testSoundboardSoundsUpsert) +} diff --git a/soundboard/models/psql_upsert.go b/soundboard/models/psql_upsert.go new file mode 100644 index 0000000000..a4a11ed93d --- /dev/null +++ b/soundboard/models/psql_upsert.go @@ -0,0 +1,61 @@ +// Code generated by SQLBoiler (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. +// This file is meant to be re-generated in place and/or deleted at any time. + +package models + +import ( + "fmt" + "strings" + + "github.com/volatiletech/sqlboiler/drivers" + "github.com/volatiletech/sqlboiler/strmangle" +) + +// buildUpsertQueryPostgres builds a SQL statement string using the upsertData provided. +func buildUpsertQueryPostgres(dia drivers.Dialect, tableName string, updateOnConflict bool, ret, update, conflict, whitelist []string) string { + conflict = strmangle.IdentQuoteSlice(dia.LQ, dia.RQ, conflict) + whitelist = strmangle.IdentQuoteSlice(dia.LQ, dia.RQ, whitelist) + ret = strmangle.IdentQuoteSlice(dia.LQ, dia.RQ, ret) + + buf := strmangle.GetBuffer() + defer strmangle.PutBuffer(buf) + + columns := "DEFAULT VALUES" + if len(whitelist) != 0 { + columns = fmt.Sprintf("(%s) VALUES (%s)", + strings.Join(whitelist, ", "), + strmangle.Placeholders(dia.UseIndexPlaceholders, len(whitelist), 1, 1)) + } + + fmt.Fprintf( + buf, + "INSERT INTO %s %s ON CONFLICT ", + tableName, + columns, + ) + + if !updateOnConflict || len(update) == 0 { + buf.WriteString("DO NOTHING") + } else { + buf.WriteByte('(') + buf.WriteString(strings.Join(conflict, ", ")) + buf.WriteString(") DO UPDATE SET ") + + for i, v := range update { + if i != 0 { + buf.WriteByte(',') + } + quoted := strmangle.IdentQuote(dia.LQ, dia.RQ, v) + buf.WriteString(quoted) + buf.WriteString(" = EXCLUDED.") + buf.WriteString(quoted) + } + } + + if len(ret) != 0 { + buf.WriteString(" RETURNING ") + buf.WriteString(strings.Join(ret, ", ")) + } + + return buf.String() +} diff --git a/soundboard/models/soundboard_sounds.go b/soundboard/models/soundboard_sounds.go new file mode 100644 index 0000000000..bcfa287801 --- /dev/null +++ b/soundboard/models/soundboard_sounds.go @@ -0,0 +1,764 @@ +// Code generated by SQLBoiler (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. +// This file is meant to be re-generated in place and/or deleted at any time. + +package models + +import ( + "context" + "database/sql" + "fmt" + "reflect" + "strconv" + "strings" + "sync" + "time" + + "github.com/pkg/errors" + "github.com/volatiletech/sqlboiler/boil" + "github.com/volatiletech/sqlboiler/queries" + "github.com/volatiletech/sqlboiler/queries/qm" + "github.com/volatiletech/sqlboiler/strmangle" + "github.com/volatiletech/sqlboiler/types" +) + +// SoundboardSound is an object representing the database table. +type SoundboardSound struct { + ID int `boil:"id" json:"id" toml:"id" yaml:"id"` + CreatedAt time.Time `boil:"created_at" json:"created_at" toml:"created_at" yaml:"created_at"` + UpdatedAt time.Time `boil:"updated_at" json:"updated_at" toml:"updated_at" yaml:"updated_at"` + GuildID int64 `boil:"guild_id" json:"guild_id" toml:"guild_id" yaml:"guild_id"` + RequiredRole string `boil:"required_role" json:"required_role" toml:"required_role" yaml:"required_role"` + Name string `boil:"name" json:"name" toml:"name" yaml:"name"` + Status int `boil:"status" json:"status" toml:"status" yaml:"status"` + RequiredRoles types.Int64Array `boil:"required_roles" json:"required_roles,omitempty" toml:"required_roles" yaml:"required_roles,omitempty"` + BlacklistedRoles types.Int64Array `boil:"blacklisted_roles" json:"blacklisted_roles,omitempty" toml:"blacklisted_roles" yaml:"blacklisted_roles,omitempty"` + + R *soundboardSoundR `boil:"-" json:"-" toml:"-" yaml:"-"` + L soundboardSoundL `boil:"-" json:"-" toml:"-" yaml:"-"` +} + +var SoundboardSoundColumns = struct { + ID string + CreatedAt string + UpdatedAt string + GuildID string + RequiredRole string + Name string + Status string + RequiredRoles string + BlacklistedRoles string +}{ + ID: "id", + CreatedAt: "created_at", + UpdatedAt: "updated_at", + GuildID: "guild_id", + RequiredRole: "required_role", + Name: "name", + Status: "status", + RequiredRoles: "required_roles", + BlacklistedRoles: "blacklisted_roles", +} + +// SoundboardSoundRels is where relationship names are stored. +var SoundboardSoundRels = struct { +}{} + +// soundboardSoundR is where relationships are stored. +type soundboardSoundR struct { +} + +// NewStruct creates a new relationship struct +func (*soundboardSoundR) NewStruct() *soundboardSoundR { + return &soundboardSoundR{} +} + +// soundboardSoundL is where Load methods for each relationship are stored. +type soundboardSoundL struct{} + +var ( + soundboardSoundColumns = []string{"id", "created_at", "updated_at", "guild_id", "required_role", "name", "status", "required_roles", "blacklisted_roles"} + soundboardSoundColumnsWithoutDefault = []string{"created_at", "updated_at", "guild_id", "required_role", "name", "status", "required_roles", "blacklisted_roles"} + soundboardSoundColumnsWithDefault = []string{"id"} + soundboardSoundPrimaryKeyColumns = []string{"id"} +) + +type ( + // SoundboardSoundSlice is an alias for a slice of pointers to SoundboardSound. + // This should generally be used opposed to []SoundboardSound. + SoundboardSoundSlice []*SoundboardSound + + soundboardSoundQuery struct { + *queries.Query + } +) + +// Cache for insert, update and upsert +var ( + soundboardSoundType = reflect.TypeOf(&SoundboardSound{}) + soundboardSoundMapping = queries.MakeStructMapping(soundboardSoundType) + soundboardSoundPrimaryKeyMapping, _ = queries.BindMapping(soundboardSoundType, soundboardSoundMapping, soundboardSoundPrimaryKeyColumns) + soundboardSoundInsertCacheMut sync.RWMutex + soundboardSoundInsertCache = make(map[string]insertCache) + soundboardSoundUpdateCacheMut sync.RWMutex + soundboardSoundUpdateCache = make(map[string]updateCache) + soundboardSoundUpsertCacheMut sync.RWMutex + soundboardSoundUpsertCache = make(map[string]insertCache) +) + +var ( + // Force time package dependency for automated UpdatedAt/CreatedAt. + _ = time.Second +) + +// OneG returns a single soundboardSound record from the query using the global executor. +func (q soundboardSoundQuery) OneG(ctx context.Context) (*SoundboardSound, error) { + return q.One(ctx, boil.GetContextDB()) +} + +// One returns a single soundboardSound record from the query. +func (q soundboardSoundQuery) One(ctx context.Context, exec boil.ContextExecutor) (*SoundboardSound, error) { + o := &SoundboardSound{} + + queries.SetLimit(q.Query, 1) + + err := q.Bind(ctx, exec, o) + if err != nil { + if errors.Cause(err) == sql.ErrNoRows { + return nil, sql.ErrNoRows + } + return nil, errors.Wrap(err, "models: failed to execute a one query for soundboard_sounds") + } + + return o, nil +} + +// AllG returns all SoundboardSound records from the query using the global executor. +func (q soundboardSoundQuery) AllG(ctx context.Context) (SoundboardSoundSlice, error) { + return q.All(ctx, boil.GetContextDB()) +} + +// All returns all SoundboardSound records from the query. +func (q soundboardSoundQuery) All(ctx context.Context, exec boil.ContextExecutor) (SoundboardSoundSlice, error) { + var o []*SoundboardSound + + err := q.Bind(ctx, exec, &o) + if err != nil { + return nil, errors.Wrap(err, "models: failed to assign all query results to SoundboardSound slice") + } + + return o, nil +} + +// CountG returns the count of all SoundboardSound records in the query, and panics on error. +func (q soundboardSoundQuery) CountG(ctx context.Context) (int64, error) { + return q.Count(ctx, boil.GetContextDB()) +} + +// Count returns the count of all SoundboardSound records in the query. +func (q soundboardSoundQuery) Count(ctx context.Context, exec boil.ContextExecutor) (int64, error) { + var count int64 + + queries.SetSelect(q.Query, nil) + queries.SetCount(q.Query) + + err := q.Query.QueryRowContext(ctx, exec).Scan(&count) + if err != nil { + return 0, errors.Wrap(err, "models: failed to count soundboard_sounds rows") + } + + return count, nil +} + +// ExistsG checks if the row exists in the table, and panics on error. +func (q soundboardSoundQuery) ExistsG(ctx context.Context) (bool, error) { + return q.Exists(ctx, boil.GetContextDB()) +} + +// Exists checks if the row exists in the table. +func (q soundboardSoundQuery) Exists(ctx context.Context, exec boil.ContextExecutor) (bool, error) { + var count int64 + + queries.SetSelect(q.Query, nil) + queries.SetCount(q.Query) + queries.SetLimit(q.Query, 1) + + err := q.Query.QueryRowContext(ctx, exec).Scan(&count) + if err != nil { + return false, errors.Wrap(err, "models: failed to check if soundboard_sounds exists") + } + + return count > 0, nil +} + +// SoundboardSounds retrieves all the records using an executor. +func SoundboardSounds(mods ...qm.QueryMod) soundboardSoundQuery { + mods = append(mods, qm.From("\"soundboard_sounds\"")) + return soundboardSoundQuery{NewQuery(mods...)} +} + +// FindSoundboardSoundG retrieves a single record by ID. +func FindSoundboardSoundG(ctx context.Context, iD int, selectCols ...string) (*SoundboardSound, error) { + return FindSoundboardSound(ctx, boil.GetContextDB(), iD, selectCols...) +} + +// FindSoundboardSound retrieves a single record by ID with an executor. +// If selectCols is empty Find will return all columns. +func FindSoundboardSound(ctx context.Context, exec boil.ContextExecutor, iD int, selectCols ...string) (*SoundboardSound, error) { + soundboardSoundObj := &SoundboardSound{} + + sel := "*" + if len(selectCols) > 0 { + sel = strings.Join(strmangle.IdentQuoteSlice(dialect.LQ, dialect.RQ, selectCols), ",") + } + query := fmt.Sprintf( + "select %s from \"soundboard_sounds\" where \"id\"=$1", sel, + ) + + q := queries.Raw(query, iD) + + err := q.Bind(ctx, exec, soundboardSoundObj) + if err != nil { + if errors.Cause(err) == sql.ErrNoRows { + return nil, sql.ErrNoRows + } + return nil, errors.Wrap(err, "models: unable to select from soundboard_sounds") + } + + return soundboardSoundObj, nil +} + +// InsertG a single record. See Insert for whitelist behavior description. +func (o *SoundboardSound) InsertG(ctx context.Context, columns boil.Columns) error { + return o.Insert(ctx, boil.GetContextDB(), columns) +} + +// Insert a single record using an executor. +// See boil.Columns.InsertColumnSet documentation to understand column list inference for inserts. +func (o *SoundboardSound) Insert(ctx context.Context, exec boil.ContextExecutor, columns boil.Columns) error { + if o == nil { + return errors.New("models: no soundboard_sounds provided for insertion") + } + + var err error + currTime := time.Now().In(boil.GetLocation()) + + if o.CreatedAt.IsZero() { + o.CreatedAt = currTime + } + if o.UpdatedAt.IsZero() { + o.UpdatedAt = currTime + } + + nzDefaults := queries.NonZeroDefaultSet(soundboardSoundColumnsWithDefault, o) + + key := makeCacheKey(columns, nzDefaults) + soundboardSoundInsertCacheMut.RLock() + cache, cached := soundboardSoundInsertCache[key] + soundboardSoundInsertCacheMut.RUnlock() + + if !cached { + wl, returnColumns := columns.InsertColumnSet( + soundboardSoundColumns, + soundboardSoundColumnsWithDefault, + soundboardSoundColumnsWithoutDefault, + nzDefaults, + ) + + cache.valueMapping, err = queries.BindMapping(soundboardSoundType, soundboardSoundMapping, wl) + if err != nil { + return err + } + cache.retMapping, err = queries.BindMapping(soundboardSoundType, soundboardSoundMapping, returnColumns) + if err != nil { + return err + } + if len(wl) != 0 { + cache.query = fmt.Sprintf("INSERT INTO \"soundboard_sounds\" (\"%s\") %%sVALUES (%s)%%s", strings.Join(wl, "\",\""), strmangle.Placeholders(dialect.UseIndexPlaceholders, len(wl), 1, 1)) + } else { + cache.query = "INSERT INTO \"soundboard_sounds\" %sDEFAULT VALUES%s" + } + + var queryOutput, queryReturning string + + if len(cache.retMapping) != 0 { + queryReturning = fmt.Sprintf(" RETURNING \"%s\"", strings.Join(returnColumns, "\",\"")) + } + + cache.query = fmt.Sprintf(cache.query, queryOutput, queryReturning) + } + + value := reflect.Indirect(reflect.ValueOf(o)) + vals := queries.ValuesFromMapping(value, cache.valueMapping) + + if boil.DebugMode { + fmt.Fprintln(boil.DebugWriter, cache.query) + fmt.Fprintln(boil.DebugWriter, vals) + } + + if len(cache.retMapping) != 0 { + err = exec.QueryRowContext(ctx, cache.query, vals...).Scan(queries.PtrsFromMapping(value, cache.retMapping)...) + } else { + _, err = exec.ExecContext(ctx, cache.query, vals...) + } + + if err != nil { + return errors.Wrap(err, "models: unable to insert into soundboard_sounds") + } + + if !cached { + soundboardSoundInsertCacheMut.Lock() + soundboardSoundInsertCache[key] = cache + soundboardSoundInsertCacheMut.Unlock() + } + + return nil +} + +// UpdateG a single SoundboardSound record using the global executor. +// See Update for more documentation. +func (o *SoundboardSound) UpdateG(ctx context.Context, columns boil.Columns) (int64, error) { + return o.Update(ctx, boil.GetContextDB(), columns) +} + +// Update uses an executor to update the SoundboardSound. +// See boil.Columns.UpdateColumnSet documentation to understand column list inference for updates. +// Update does not automatically update the record in case of default values. Use .Reload() to refresh the records. +func (o *SoundboardSound) Update(ctx context.Context, exec boil.ContextExecutor, columns boil.Columns) (int64, error) { + currTime := time.Now().In(boil.GetLocation()) + + o.UpdatedAt = currTime + + var err error + key := makeCacheKey(columns, nil) + soundboardSoundUpdateCacheMut.RLock() + cache, cached := soundboardSoundUpdateCache[key] + soundboardSoundUpdateCacheMut.RUnlock() + + if !cached { + wl := columns.UpdateColumnSet( + soundboardSoundColumns, + soundboardSoundPrimaryKeyColumns, + ) + + if !columns.IsWhitelist() { + wl = strmangle.SetComplement(wl, []string{"created_at"}) + } + if len(wl) == 0 { + return 0, errors.New("models: unable to update soundboard_sounds, could not build whitelist") + } + + cache.query = fmt.Sprintf("UPDATE \"soundboard_sounds\" SET %s WHERE %s", + strmangle.SetParamNames("\"", "\"", 1, wl), + strmangle.WhereClause("\"", "\"", len(wl)+1, soundboardSoundPrimaryKeyColumns), + ) + cache.valueMapping, err = queries.BindMapping(soundboardSoundType, soundboardSoundMapping, append(wl, soundboardSoundPrimaryKeyColumns...)) + if err != nil { + return 0, err + } + } + + values := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(o)), cache.valueMapping) + + if boil.DebugMode { + fmt.Fprintln(boil.DebugWriter, cache.query) + fmt.Fprintln(boil.DebugWriter, values) + } + + var result sql.Result + result, err = exec.ExecContext(ctx, cache.query, values...) + if err != nil { + return 0, errors.Wrap(err, "models: unable to update soundboard_sounds row") + } + + rowsAff, err := result.RowsAffected() + if err != nil { + return 0, errors.Wrap(err, "models: failed to get rows affected by update for soundboard_sounds") + } + + if !cached { + soundboardSoundUpdateCacheMut.Lock() + soundboardSoundUpdateCache[key] = cache + soundboardSoundUpdateCacheMut.Unlock() + } + + return rowsAff, nil +} + +// UpdateAllG updates all rows with the specified column values. +func (q soundboardSoundQuery) UpdateAllG(ctx context.Context, cols M) (int64, error) { + return q.UpdateAll(ctx, boil.GetContextDB(), cols) +} + +// UpdateAll updates all rows with the specified column values. +func (q soundboardSoundQuery) UpdateAll(ctx context.Context, exec boil.ContextExecutor, cols M) (int64, error) { + queries.SetUpdate(q.Query, cols) + + result, err := q.Query.ExecContext(ctx, exec) + if err != nil { + return 0, errors.Wrap(err, "models: unable to update all for soundboard_sounds") + } + + rowsAff, err := result.RowsAffected() + if err != nil { + return 0, errors.Wrap(err, "models: unable to retrieve rows affected for soundboard_sounds") + } + + return rowsAff, nil +} + +// UpdateAllG updates all rows with the specified column values. +func (o SoundboardSoundSlice) UpdateAllG(ctx context.Context, cols M) (int64, error) { + return o.UpdateAll(ctx, boil.GetContextDB(), cols) +} + +// UpdateAll updates all rows with the specified column values, using an executor. +func (o SoundboardSoundSlice) UpdateAll(ctx context.Context, exec boil.ContextExecutor, cols M) (int64, error) { + ln := int64(len(o)) + if ln == 0 { + return 0, nil + } + + if len(cols) == 0 { + return 0, errors.New("models: update all requires at least one column argument") + } + + colNames := make([]string, len(cols)) + args := make([]interface{}, len(cols)) + + i := 0 + for name, value := range cols { + colNames[i] = name + args[i] = value + i++ + } + + // Append all of the primary key values for each column + for _, obj := range o { + pkeyArgs := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(obj)), soundboardSoundPrimaryKeyMapping) + args = append(args, pkeyArgs...) + } + + sql := fmt.Sprintf("UPDATE \"soundboard_sounds\" SET %s WHERE %s", + strmangle.SetParamNames("\"", "\"", 1, colNames), + strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), len(colNames)+1, soundboardSoundPrimaryKeyColumns, len(o))) + + if boil.DebugMode { + fmt.Fprintln(boil.DebugWriter, sql) + fmt.Fprintln(boil.DebugWriter, args...) + } + + result, err := exec.ExecContext(ctx, sql, args...) + if err != nil { + return 0, errors.Wrap(err, "models: unable to update all in soundboardSound slice") + } + + rowsAff, err := result.RowsAffected() + if err != nil { + return 0, errors.Wrap(err, "models: unable to retrieve rows affected all in update all soundboardSound") + } + return rowsAff, nil +} + +// UpsertG attempts an insert, and does an update or ignore on conflict. +func (o *SoundboardSound) UpsertG(ctx context.Context, updateOnConflict bool, conflictColumns []string, updateColumns, insertColumns boil.Columns) error { + return o.Upsert(ctx, boil.GetContextDB(), updateOnConflict, conflictColumns, updateColumns, insertColumns) +} + +// Upsert attempts an insert using an executor, and does an update or ignore on conflict. +// See boil.Columns documentation for how to properly use updateColumns and insertColumns. +func (o *SoundboardSound) Upsert(ctx context.Context, exec boil.ContextExecutor, updateOnConflict bool, conflictColumns []string, updateColumns, insertColumns boil.Columns) error { + if o == nil { + return errors.New("models: no soundboard_sounds provided for upsert") + } + currTime := time.Now().In(boil.GetLocation()) + + if o.CreatedAt.IsZero() { + o.CreatedAt = currTime + } + o.UpdatedAt = currTime + + nzDefaults := queries.NonZeroDefaultSet(soundboardSoundColumnsWithDefault, o) + + // Build cache key in-line uglily - mysql vs psql problems + buf := strmangle.GetBuffer() + if updateOnConflict { + buf.WriteByte('t') + } else { + buf.WriteByte('f') + } + buf.WriteByte('.') + for _, c := range conflictColumns { + buf.WriteString(c) + } + buf.WriteByte('.') + buf.WriteString(strconv.Itoa(updateColumns.Kind)) + for _, c := range updateColumns.Cols { + buf.WriteString(c) + } + buf.WriteByte('.') + buf.WriteString(strconv.Itoa(insertColumns.Kind)) + for _, c := range insertColumns.Cols { + buf.WriteString(c) + } + buf.WriteByte('.') + for _, c := range nzDefaults { + buf.WriteString(c) + } + key := buf.String() + strmangle.PutBuffer(buf) + + soundboardSoundUpsertCacheMut.RLock() + cache, cached := soundboardSoundUpsertCache[key] + soundboardSoundUpsertCacheMut.RUnlock() + + var err error + + if !cached { + insert, ret := insertColumns.InsertColumnSet( + soundboardSoundColumns, + soundboardSoundColumnsWithDefault, + soundboardSoundColumnsWithoutDefault, + nzDefaults, + ) + update := updateColumns.UpdateColumnSet( + soundboardSoundColumns, + soundboardSoundPrimaryKeyColumns, + ) + + if len(update) == 0 { + return errors.New("models: unable to upsert soundboard_sounds, could not build update column list") + } + + conflict := conflictColumns + if len(conflict) == 0 { + conflict = make([]string, len(soundboardSoundPrimaryKeyColumns)) + copy(conflict, soundboardSoundPrimaryKeyColumns) + } + cache.query = buildUpsertQueryPostgres(dialect, "\"soundboard_sounds\"", updateOnConflict, ret, update, conflict, insert) + + cache.valueMapping, err = queries.BindMapping(soundboardSoundType, soundboardSoundMapping, insert) + if err != nil { + return err + } + if len(ret) != 0 { + cache.retMapping, err = queries.BindMapping(soundboardSoundType, soundboardSoundMapping, ret) + if err != nil { + return err + } + } + } + + value := reflect.Indirect(reflect.ValueOf(o)) + vals := queries.ValuesFromMapping(value, cache.valueMapping) + var returns []interface{} + if len(cache.retMapping) != 0 { + returns = queries.PtrsFromMapping(value, cache.retMapping) + } + + if boil.DebugMode { + fmt.Fprintln(boil.DebugWriter, cache.query) + fmt.Fprintln(boil.DebugWriter, vals) + } + + if len(cache.retMapping) != 0 { + err = exec.QueryRowContext(ctx, cache.query, vals...).Scan(returns...) + if err == sql.ErrNoRows { + err = nil // Postgres doesn't return anything when there's no update + } + } else { + _, err = exec.ExecContext(ctx, cache.query, vals...) + } + if err != nil { + return errors.Wrap(err, "models: unable to upsert soundboard_sounds") + } + + if !cached { + soundboardSoundUpsertCacheMut.Lock() + soundboardSoundUpsertCache[key] = cache + soundboardSoundUpsertCacheMut.Unlock() + } + + return nil +} + +// DeleteG deletes a single SoundboardSound record. +// DeleteG will match against the primary key column to find the record to delete. +func (o *SoundboardSound) DeleteG(ctx context.Context) (int64, error) { + return o.Delete(ctx, boil.GetContextDB()) +} + +// Delete deletes a single SoundboardSound record with an executor. +// Delete will match against the primary key column to find the record to delete. +func (o *SoundboardSound) Delete(ctx context.Context, exec boil.ContextExecutor) (int64, error) { + if o == nil { + return 0, errors.New("models: no SoundboardSound provided for delete") + } + + args := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(o)), soundboardSoundPrimaryKeyMapping) + sql := "DELETE FROM \"soundboard_sounds\" WHERE \"id\"=$1" + + if boil.DebugMode { + fmt.Fprintln(boil.DebugWriter, sql) + fmt.Fprintln(boil.DebugWriter, args...) + } + + result, err := exec.ExecContext(ctx, sql, args...) + if err != nil { + return 0, errors.Wrap(err, "models: unable to delete from soundboard_sounds") + } + + rowsAff, err := result.RowsAffected() + if err != nil { + return 0, errors.Wrap(err, "models: failed to get rows affected by delete for soundboard_sounds") + } + + return rowsAff, nil +} + +// DeleteAll deletes all matching rows. +func (q soundboardSoundQuery) DeleteAll(ctx context.Context, exec boil.ContextExecutor) (int64, error) { + if q.Query == nil { + return 0, errors.New("models: no soundboardSoundQuery provided for delete all") + } + + queries.SetDelete(q.Query) + + result, err := q.Query.ExecContext(ctx, exec) + if err != nil { + return 0, errors.Wrap(err, "models: unable to delete all from soundboard_sounds") + } + + rowsAff, err := result.RowsAffected() + if err != nil { + return 0, errors.Wrap(err, "models: failed to get rows affected by deleteall for soundboard_sounds") + } + + return rowsAff, nil +} + +// DeleteAllG deletes all rows in the slice. +func (o SoundboardSoundSlice) DeleteAllG(ctx context.Context) (int64, error) { + return o.DeleteAll(ctx, boil.GetContextDB()) +} + +// DeleteAll deletes all rows in the slice, using an executor. +func (o SoundboardSoundSlice) DeleteAll(ctx context.Context, exec boil.ContextExecutor) (int64, error) { + if o == nil { + return 0, errors.New("models: no SoundboardSound slice provided for delete all") + } + + if len(o) == 0 { + return 0, nil + } + + var args []interface{} + for _, obj := range o { + pkeyArgs := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(obj)), soundboardSoundPrimaryKeyMapping) + args = append(args, pkeyArgs...) + } + + sql := "DELETE FROM \"soundboard_sounds\" WHERE " + + strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), 1, soundboardSoundPrimaryKeyColumns, len(o)) + + if boil.DebugMode { + fmt.Fprintln(boil.DebugWriter, sql) + fmt.Fprintln(boil.DebugWriter, args) + } + + result, err := exec.ExecContext(ctx, sql, args...) + if err != nil { + return 0, errors.Wrap(err, "models: unable to delete all from soundboardSound slice") + } + + rowsAff, err := result.RowsAffected() + if err != nil { + return 0, errors.Wrap(err, "models: failed to get rows affected by deleteall for soundboard_sounds") + } + + return rowsAff, nil +} + +// ReloadG refetches the object from the database using the primary keys. +func (o *SoundboardSound) ReloadG(ctx context.Context) error { + if o == nil { + return errors.New("models: no SoundboardSound provided for reload") + } + + return o.Reload(ctx, boil.GetContextDB()) +} + +// Reload refetches the object from the database +// using the primary keys with an executor. +func (o *SoundboardSound) Reload(ctx context.Context, exec boil.ContextExecutor) error { + ret, err := FindSoundboardSound(ctx, exec, o.ID) + if err != nil { + return err + } + + *o = *ret + return nil +} + +// ReloadAllG refetches every row with matching primary key column values +// and overwrites the original object slice with the newly updated slice. +func (o *SoundboardSoundSlice) ReloadAllG(ctx context.Context) error { + if o == nil { + return errors.New("models: empty SoundboardSoundSlice provided for reload all") + } + + return o.ReloadAll(ctx, boil.GetContextDB()) +} + +// ReloadAll refetches every row with matching primary key column values +// and overwrites the original object slice with the newly updated slice. +func (o *SoundboardSoundSlice) ReloadAll(ctx context.Context, exec boil.ContextExecutor) error { + if o == nil || len(*o) == 0 { + return nil + } + + slice := SoundboardSoundSlice{} + var args []interface{} + for _, obj := range *o { + pkeyArgs := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(obj)), soundboardSoundPrimaryKeyMapping) + args = append(args, pkeyArgs...) + } + + sql := "SELECT \"soundboard_sounds\".* FROM \"soundboard_sounds\" WHERE " + + strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), 1, soundboardSoundPrimaryKeyColumns, len(*o)) + + q := queries.Raw(sql, args...) + + err := q.Bind(ctx, exec, &slice) + if err != nil { + return errors.Wrap(err, "models: unable to reload all in SoundboardSoundSlice") + } + + *o = slice + + return nil +} + +// SoundboardSoundExistsG checks if the SoundboardSound row exists. +func SoundboardSoundExistsG(ctx context.Context, iD int) (bool, error) { + return SoundboardSoundExists(ctx, boil.GetContextDB(), iD) +} + +// SoundboardSoundExists checks if the SoundboardSound row exists. +func SoundboardSoundExists(ctx context.Context, exec boil.ContextExecutor, iD int) (bool, error) { + var exists bool + sql := "select exists(select 1 from \"soundboard_sounds\" where \"id\"=$1 limit 1)" + + if boil.DebugMode { + fmt.Fprintln(boil.DebugWriter, sql) + fmt.Fprintln(boil.DebugWriter, iD) + } + + row := exec.QueryRowContext(ctx, sql, iD) + + err := row.Scan(&exists) + if err != nil { + return false, errors.Wrap(err, "models: unable to check if soundboard_sounds exists") + } + + return exists, nil +} diff --git a/soundboard/models/soundboard_sounds_test.go b/soundboard/models/soundboard_sounds_test.go new file mode 100644 index 0000000000..b54c6c44ae --- /dev/null +++ b/soundboard/models/soundboard_sounds_test.go @@ -0,0 +1,591 @@ +// Code generated by SQLBoiler (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. +// This file is meant to be re-generated in place and/or deleted at any time. + +package models + +import ( + "bytes" + "context" + "reflect" + "testing" + + "github.com/volatiletech/sqlboiler/boil" + "github.com/volatiletech/sqlboiler/queries" + "github.com/volatiletech/sqlboiler/randomize" + "github.com/volatiletech/sqlboiler/strmangle" +) + +var ( + // Relationships sometimes use the reflection helper queries.Equal/queries.Assign + // so force a package dependency in case they don't. + _ = queries.Equal +) + +func testSoundboardSounds(t *testing.T) { + t.Parallel() + + query := SoundboardSounds() + + if query.Query == nil { + t.Error("expected a query, got nothing") + } +} + +func testSoundboardSoundsDelete(t *testing.T) { + t.Parallel() + + seed := randomize.NewSeed() + var err error + o := &SoundboardSound{} + if err = randomize.Struct(seed, o, soundboardSoundDBTypes, true, soundboardSoundColumnsWithDefault...); err != nil { + t.Errorf("Unable to randomize SoundboardSound struct: %s", err) + } + + ctx := context.Background() + tx := MustTx(boil.BeginTx(ctx, nil)) + defer func() { _ = tx.Rollback() }() + if err = o.Insert(ctx, tx, boil.Infer()); err != nil { + t.Error(err) + } + + if rowsAff, err := o.Delete(ctx, tx); err != nil { + t.Error(err) + } else if rowsAff != 1 { + t.Error("should only have deleted one row, but affected:", rowsAff) + } + + count, err := SoundboardSounds().Count(ctx, tx) + if err != nil { + t.Error(err) + } + + if count != 0 { + t.Error("want zero records, got:", count) + } +} + +func testSoundboardSoundsQueryDeleteAll(t *testing.T) { + t.Parallel() + + seed := randomize.NewSeed() + var err error + o := &SoundboardSound{} + if err = randomize.Struct(seed, o, soundboardSoundDBTypes, true, soundboardSoundColumnsWithDefault...); err != nil { + t.Errorf("Unable to randomize SoundboardSound struct: %s", err) + } + + ctx := context.Background() + tx := MustTx(boil.BeginTx(ctx, nil)) + defer func() { _ = tx.Rollback() }() + if err = o.Insert(ctx, tx, boil.Infer()); err != nil { + t.Error(err) + } + + if rowsAff, err := SoundboardSounds().DeleteAll(ctx, tx); err != nil { + t.Error(err) + } else if rowsAff != 1 { + t.Error("should only have deleted one row, but affected:", rowsAff) + } + + count, err := SoundboardSounds().Count(ctx, tx) + if err != nil { + t.Error(err) + } + + if count != 0 { + t.Error("want zero records, got:", count) + } +} + +func testSoundboardSoundsSliceDeleteAll(t *testing.T) { + t.Parallel() + + seed := randomize.NewSeed() + var err error + o := &SoundboardSound{} + if err = randomize.Struct(seed, o, soundboardSoundDBTypes, true, soundboardSoundColumnsWithDefault...); err != nil { + t.Errorf("Unable to randomize SoundboardSound struct: %s", err) + } + + ctx := context.Background() + tx := MustTx(boil.BeginTx(ctx, nil)) + defer func() { _ = tx.Rollback() }() + if err = o.Insert(ctx, tx, boil.Infer()); err != nil { + t.Error(err) + } + + slice := SoundboardSoundSlice{o} + + if rowsAff, err := slice.DeleteAll(ctx, tx); err != nil { + t.Error(err) + } else if rowsAff != 1 { + t.Error("should only have deleted one row, but affected:", rowsAff) + } + + count, err := SoundboardSounds().Count(ctx, tx) + if err != nil { + t.Error(err) + } + + if count != 0 { + t.Error("want zero records, got:", count) + } +} + +func testSoundboardSoundsExists(t *testing.T) { + t.Parallel() + + seed := randomize.NewSeed() + var err error + o := &SoundboardSound{} + if err = randomize.Struct(seed, o, soundboardSoundDBTypes, true, soundboardSoundColumnsWithDefault...); err != nil { + t.Errorf("Unable to randomize SoundboardSound struct: %s", err) + } + + ctx := context.Background() + tx := MustTx(boil.BeginTx(ctx, nil)) + defer func() { _ = tx.Rollback() }() + if err = o.Insert(ctx, tx, boil.Infer()); err != nil { + t.Error(err) + } + + e, err := SoundboardSoundExists(ctx, tx, o.ID) + if err != nil { + t.Errorf("Unable to check if SoundboardSound exists: %s", err) + } + if !e { + t.Errorf("Expected SoundboardSoundExists to return true, but got false.") + } +} + +func testSoundboardSoundsFind(t *testing.T) { + t.Parallel() + + seed := randomize.NewSeed() + var err error + o := &SoundboardSound{} + if err = randomize.Struct(seed, o, soundboardSoundDBTypes, true, soundboardSoundColumnsWithDefault...); err != nil { + t.Errorf("Unable to randomize SoundboardSound struct: %s", err) + } + + ctx := context.Background() + tx := MustTx(boil.BeginTx(ctx, nil)) + defer func() { _ = tx.Rollback() }() + if err = o.Insert(ctx, tx, boil.Infer()); err != nil { + t.Error(err) + } + + soundboardSoundFound, err := FindSoundboardSound(ctx, tx, o.ID) + if err != nil { + t.Error(err) + } + + if soundboardSoundFound == nil { + t.Error("want a record, got nil") + } +} + +func testSoundboardSoundsBind(t *testing.T) { + t.Parallel() + + seed := randomize.NewSeed() + var err error + o := &SoundboardSound{} + if err = randomize.Struct(seed, o, soundboardSoundDBTypes, true, soundboardSoundColumnsWithDefault...); err != nil { + t.Errorf("Unable to randomize SoundboardSound struct: %s", err) + } + + ctx := context.Background() + tx := MustTx(boil.BeginTx(ctx, nil)) + defer func() { _ = tx.Rollback() }() + if err = o.Insert(ctx, tx, boil.Infer()); err != nil { + t.Error(err) + } + + if err = SoundboardSounds().Bind(ctx, tx, o); err != nil { + t.Error(err) + } +} + +func testSoundboardSoundsOne(t *testing.T) { + t.Parallel() + + seed := randomize.NewSeed() + var err error + o := &SoundboardSound{} + if err = randomize.Struct(seed, o, soundboardSoundDBTypes, true, soundboardSoundColumnsWithDefault...); err != nil { + t.Errorf("Unable to randomize SoundboardSound struct: %s", err) + } + + ctx := context.Background() + tx := MustTx(boil.BeginTx(ctx, nil)) + defer func() { _ = tx.Rollback() }() + if err = o.Insert(ctx, tx, boil.Infer()); err != nil { + t.Error(err) + } + + if x, err := SoundboardSounds().One(ctx, tx); err != nil { + t.Error(err) + } else if x == nil { + t.Error("expected to get a non nil record") + } +} + +func testSoundboardSoundsAll(t *testing.T) { + t.Parallel() + + seed := randomize.NewSeed() + var err error + soundboardSoundOne := &SoundboardSound{} + soundboardSoundTwo := &SoundboardSound{} + if err = randomize.Struct(seed, soundboardSoundOne, soundboardSoundDBTypes, false, soundboardSoundColumnsWithDefault...); err != nil { + t.Errorf("Unable to randomize SoundboardSound struct: %s", err) + } + if err = randomize.Struct(seed, soundboardSoundTwo, soundboardSoundDBTypes, false, soundboardSoundColumnsWithDefault...); err != nil { + t.Errorf("Unable to randomize SoundboardSound struct: %s", err) + } + + ctx := context.Background() + tx := MustTx(boil.BeginTx(ctx, nil)) + defer func() { _ = tx.Rollback() }() + if err = soundboardSoundOne.Insert(ctx, tx, boil.Infer()); err != nil { + t.Error(err) + } + if err = soundboardSoundTwo.Insert(ctx, tx, boil.Infer()); err != nil { + t.Error(err) + } + + slice, err := SoundboardSounds().All(ctx, tx) + if err != nil { + t.Error(err) + } + + if len(slice) != 2 { + t.Error("want 2 records, got:", len(slice)) + } +} + +func testSoundboardSoundsCount(t *testing.T) { + t.Parallel() + + var err error + seed := randomize.NewSeed() + soundboardSoundOne := &SoundboardSound{} + soundboardSoundTwo := &SoundboardSound{} + if err = randomize.Struct(seed, soundboardSoundOne, soundboardSoundDBTypes, false, soundboardSoundColumnsWithDefault...); err != nil { + t.Errorf("Unable to randomize SoundboardSound struct: %s", err) + } + if err = randomize.Struct(seed, soundboardSoundTwo, soundboardSoundDBTypes, false, soundboardSoundColumnsWithDefault...); err != nil { + t.Errorf("Unable to randomize SoundboardSound struct: %s", err) + } + + ctx := context.Background() + tx := MustTx(boil.BeginTx(ctx, nil)) + defer func() { _ = tx.Rollback() }() + if err = soundboardSoundOne.Insert(ctx, tx, boil.Infer()); err != nil { + t.Error(err) + } + if err = soundboardSoundTwo.Insert(ctx, tx, boil.Infer()); err != nil { + t.Error(err) + } + + count, err := SoundboardSounds().Count(ctx, tx) + if err != nil { + t.Error(err) + } + + if count != 2 { + t.Error("want 2 records, got:", count) + } +} + +func testSoundboardSoundsInsert(t *testing.T) { + t.Parallel() + + seed := randomize.NewSeed() + var err error + o := &SoundboardSound{} + if err = randomize.Struct(seed, o, soundboardSoundDBTypes, true, soundboardSoundColumnsWithDefault...); err != nil { + t.Errorf("Unable to randomize SoundboardSound struct: %s", err) + } + + ctx := context.Background() + tx := MustTx(boil.BeginTx(ctx, nil)) + defer func() { _ = tx.Rollback() }() + if err = o.Insert(ctx, tx, boil.Infer()); err != nil { + t.Error(err) + } + + count, err := SoundboardSounds().Count(ctx, tx) + if err != nil { + t.Error(err) + } + + if count != 1 { + t.Error("want one record, got:", count) + } +} + +func testSoundboardSoundsInsertWhitelist(t *testing.T) { + t.Parallel() + + seed := randomize.NewSeed() + var err error + o := &SoundboardSound{} + if err = randomize.Struct(seed, o, soundboardSoundDBTypes, true); err != nil { + t.Errorf("Unable to randomize SoundboardSound struct: %s", err) + } + + ctx := context.Background() + tx := MustTx(boil.BeginTx(ctx, nil)) + defer func() { _ = tx.Rollback() }() + if err = o.Insert(ctx, tx, boil.Whitelist(soundboardSoundColumnsWithoutDefault...)); err != nil { + t.Error(err) + } + + count, err := SoundboardSounds().Count(ctx, tx) + if err != nil { + t.Error(err) + } + + if count != 1 { + t.Error("want one record, got:", count) + } +} + +func testSoundboardSoundsReload(t *testing.T) { + t.Parallel() + + seed := randomize.NewSeed() + var err error + o := &SoundboardSound{} + if err = randomize.Struct(seed, o, soundboardSoundDBTypes, true, soundboardSoundColumnsWithDefault...); err != nil { + t.Errorf("Unable to randomize SoundboardSound struct: %s", err) + } + + ctx := context.Background() + tx := MustTx(boil.BeginTx(ctx, nil)) + defer func() { _ = tx.Rollback() }() + if err = o.Insert(ctx, tx, boil.Infer()); err != nil { + t.Error(err) + } + + if err = o.Reload(ctx, tx); err != nil { + t.Error(err) + } +} + +func testSoundboardSoundsReloadAll(t *testing.T) { + t.Parallel() + + seed := randomize.NewSeed() + var err error + o := &SoundboardSound{} + if err = randomize.Struct(seed, o, soundboardSoundDBTypes, true, soundboardSoundColumnsWithDefault...); err != nil { + t.Errorf("Unable to randomize SoundboardSound struct: %s", err) + } + + ctx := context.Background() + tx := MustTx(boil.BeginTx(ctx, nil)) + defer func() { _ = tx.Rollback() }() + if err = o.Insert(ctx, tx, boil.Infer()); err != nil { + t.Error(err) + } + + slice := SoundboardSoundSlice{o} + + if err = slice.ReloadAll(ctx, tx); err != nil { + t.Error(err) + } +} + +func testSoundboardSoundsSelect(t *testing.T) { + t.Parallel() + + seed := randomize.NewSeed() + var err error + o := &SoundboardSound{} + if err = randomize.Struct(seed, o, soundboardSoundDBTypes, true, soundboardSoundColumnsWithDefault...); err != nil { + t.Errorf("Unable to randomize SoundboardSound struct: %s", err) + } + + ctx := context.Background() + tx := MustTx(boil.BeginTx(ctx, nil)) + defer func() { _ = tx.Rollback() }() + if err = o.Insert(ctx, tx, boil.Infer()); err != nil { + t.Error(err) + } + + slice, err := SoundboardSounds().All(ctx, tx) + if err != nil { + t.Error(err) + } + + if len(slice) != 1 { + t.Error("want one record, got:", len(slice)) + } +} + +var ( + soundboardSoundDBTypes = map[string]string{`BlacklistedRoles`: `ARRAYbigint`, `CreatedAt`: `timestamp with time zone`, `GuildID`: `bigint`, `ID`: `integer`, `Name`: `text`, `RequiredRole`: `text`, `RequiredRoles`: `ARRAYbigint`, `Status`: `integer`, `UpdatedAt`: `timestamp with time zone`} + _ = bytes.MinRead +) + +func testSoundboardSoundsUpdate(t *testing.T) { + t.Parallel() + + if 0 == len(soundboardSoundPrimaryKeyColumns) { + t.Skip("Skipping table with no primary key columns") + } + if len(soundboardSoundColumns) == len(soundboardSoundPrimaryKeyColumns) { + t.Skip("Skipping table with only primary key columns") + } + + seed := randomize.NewSeed() + var err error + o := &SoundboardSound{} + if err = randomize.Struct(seed, o, soundboardSoundDBTypes, true, soundboardSoundColumnsWithDefault...); err != nil { + t.Errorf("Unable to randomize SoundboardSound struct: %s", err) + } + + ctx := context.Background() + tx := MustTx(boil.BeginTx(ctx, nil)) + defer func() { _ = tx.Rollback() }() + if err = o.Insert(ctx, tx, boil.Infer()); err != nil { + t.Error(err) + } + + count, err := SoundboardSounds().Count(ctx, tx) + if err != nil { + t.Error(err) + } + + if count != 1 { + t.Error("want one record, got:", count) + } + + if err = randomize.Struct(seed, o, soundboardSoundDBTypes, true, soundboardSoundPrimaryKeyColumns...); err != nil { + t.Errorf("Unable to randomize SoundboardSound struct: %s", err) + } + + if rowsAff, err := o.Update(ctx, tx, boil.Infer()); err != nil { + t.Error(err) + } else if rowsAff != 1 { + t.Error("should only affect one row but affected", rowsAff) + } +} + +func testSoundboardSoundsSliceUpdateAll(t *testing.T) { + t.Parallel() + + if len(soundboardSoundColumns) == len(soundboardSoundPrimaryKeyColumns) { + t.Skip("Skipping table with only primary key columns") + } + + seed := randomize.NewSeed() + var err error + o := &SoundboardSound{} + if err = randomize.Struct(seed, o, soundboardSoundDBTypes, true, soundboardSoundColumnsWithDefault...); err != nil { + t.Errorf("Unable to randomize SoundboardSound struct: %s", err) + } + + ctx := context.Background() + tx := MustTx(boil.BeginTx(ctx, nil)) + defer func() { _ = tx.Rollback() }() + if err = o.Insert(ctx, tx, boil.Infer()); err != nil { + t.Error(err) + } + + count, err := SoundboardSounds().Count(ctx, tx) + if err != nil { + t.Error(err) + } + + if count != 1 { + t.Error("want one record, got:", count) + } + + if err = randomize.Struct(seed, o, soundboardSoundDBTypes, true, soundboardSoundPrimaryKeyColumns...); err != nil { + t.Errorf("Unable to randomize SoundboardSound struct: %s", err) + } + + // Remove Primary keys and unique columns from what we plan to update + var fields []string + if strmangle.StringSliceMatch(soundboardSoundColumns, soundboardSoundPrimaryKeyColumns) { + fields = soundboardSoundColumns + } else { + fields = strmangle.SetComplement( + soundboardSoundColumns, + soundboardSoundPrimaryKeyColumns, + ) + } + + value := reflect.Indirect(reflect.ValueOf(o)) + typ := reflect.TypeOf(o).Elem() + n := typ.NumField() + + updateMap := M{} + for _, col := range fields { + for i := 0; i < n; i++ { + f := typ.Field(i) + if f.Tag.Get("boil") == col { + updateMap[col] = value.Field(i).Interface() + } + } + } + + slice := SoundboardSoundSlice{o} + if rowsAff, err := slice.UpdateAll(ctx, tx, updateMap); err != nil { + t.Error(err) + } else if rowsAff != 1 { + t.Error("wanted one record updated but got", rowsAff) + } +} + +func testSoundboardSoundsUpsert(t *testing.T) { + t.Parallel() + + if len(soundboardSoundColumns) == len(soundboardSoundPrimaryKeyColumns) { + t.Skip("Skipping table with only primary key columns") + } + + seed := randomize.NewSeed() + var err error + // Attempt the INSERT side of an UPSERT + o := SoundboardSound{} + if err = randomize.Struct(seed, &o, soundboardSoundDBTypes, true); err != nil { + t.Errorf("Unable to randomize SoundboardSound struct: %s", err) + } + + ctx := context.Background() + tx := MustTx(boil.BeginTx(ctx, nil)) + defer func() { _ = tx.Rollback() }() + if err = o.Upsert(ctx, tx, false, nil, boil.Infer(), boil.Infer()); err != nil { + t.Errorf("Unable to upsert SoundboardSound: %s", err) + } + + count, err := SoundboardSounds().Count(ctx, tx) + if err != nil { + t.Error(err) + } + if count != 1 { + t.Error("want one record, got:", count) + } + + // Attempt the UPDATE side of an UPSERT + if err = randomize.Struct(seed, &o, soundboardSoundDBTypes, false, soundboardSoundPrimaryKeyColumns...); err != nil { + t.Errorf("Unable to randomize SoundboardSound struct: %s", err) + } + + if err = o.Upsert(ctx, tx, true, nil, boil.Infer(), boil.Infer()); err != nil { + t.Errorf("Unable to upsert SoundboardSound: %s", err) + } + + count, err = SoundboardSounds().Count(ctx, tx) + if err != nil { + t.Error(err) + } + if count != 1 { + t.Error("want one record, got:", count) + } +} diff --git a/soundboard/player.go b/soundboard/player.go index 68f4636d49..a29f60f9f0 100644 --- a/soundboard/player.go +++ b/soundboard/player.go @@ -17,7 +17,7 @@ type PlayRequest struct { ChannelID int64 GuildID int64 CommandRanFrom int64 - Sound uint + Sound int } var ( @@ -27,7 +27,7 @@ var ( ) // RequestPlaySound either queues up a sound to be played in an existing player or creates a new one -func RequestPlaySound(guildID int64, channelID, channelRanFrom int64, soundID uint) (queued bool) { +func RequestPlaySound(guildID int64, channelID, channelRanFrom int64, soundID int) (queued bool) { item := &PlayRequest{ ChannelID: channelID, GuildID: guildID, diff --git a/soundboard/schema.go b/soundboard/schema.go new file mode 100644 index 0000000000..3d76806327 --- /dev/null +++ b/soundboard/schema.go @@ -0,0 +1,45 @@ +package soundboard + +const DBSchema = ` +CREATE TABLE IF NOT EXISTS soundboard_sounds( + id SERIAL PRIMARY KEY, + created_at TIMESTAMP WITH TIME ZONE NOT NULL, + updated_at TIMESTAMP WITH TIME ZONE NOT NULL, + + guild_id BIGINT NOT NULL, + required_role TEXT NOT NULL, + name TEXT NOT NULL, + status INT NOT NULL, + + required_roles BIGINT[], + blacklisted_roles BIGINT[] +); + +CREATE INDEX IF NOT EXISTS soundboard_sounds_guild_idx ON soundboard_sounds(guild_id); + +-- i was using gorm way back, and that apperently didn't add not null constraints +-- so for existing tables make sure they're present + +-- this SHOULD be impossible, but just to sure... (was 0 rows on prod db) +DELETE FROM soundboard_sounds WHERE guild_id IS NULL or required_role IS NULL or name IS NULL or status IS NULL or created_at IS NULL or updated_at IS NULL; + +ALTER TABLE soundboard_sounds ALTER COLUMN guild_id SET NOT NULL; +ALTER TABLE soundboard_sounds ALTER COLUMN required_role SET NOT NULL; +ALTER TABLE soundboard_sounds ALTER COLUMN name SET NOT NULL; +ALTER TABLE soundboard_sounds ALTER COLUMN status SET NOT NULL; +ALTER TABLE soundboard_sounds ALTER COLUMN created_at SET NOT NULL; +ALTER TABLE soundboard_sounds ALTER COLUMN updated_at SET NOT NULL; + + +-- we migrate the data from the old system so that peoples settings dont dissapear +DO $$ +BEGIN + IF (SELECT COUNT(*) FROM information_schema.columns WHERE table_name='soundboard_sounds' and column_name='required_roles') < 1 THEN + ALTER TABLE soundboard_sounds ADD COLUMN required_roles BIGINT[]; + ALTER TABLE soundboard_sounds ADD COLUMN blacklisted_roles BIGINT[]; + + -- migrate + UPDATE soundboard_sounds SET required_roles=ARRAY[required_role]::BIGINT[] WHERE required_role IS NOT NULL AND required_role != ''; + END IF; +END $$; +` diff --git a/soundboard/soundboard.go b/soundboard/soundboard.go index e759b1ce95..9a7201e80b 100644 --- a/soundboard/soundboard.go +++ b/soundboard/soundboard.go @@ -1,64 +1,37 @@ package soundboard +//go:generate sqlboiler --no-hooks psql + import ( "fmt" - "github.com/jinzhu/gorm" "github.com/jonas747/discordgo" "github.com/jonas747/yagpdb/common" - "github.com/jonas747/yagpdb/common/configstore" "github.com/jonas747/yagpdb/premium" + "github.com/jonas747/yagpdb/soundboard/models" + "github.com/sirupsen/logrus" + "github.com/volatiletech/sqlboiler/queries/qm" "golang.org/x/net/context" "os" ) type Plugin struct{} -// GetGuildConfig returns a GuildConfig item from db -func (p *Plugin) GetGuildConfig(ctx context.Context, guildID int64, dest configstore.GuildConfig) (err error) { - cast := dest.(*SoundboardConfig) - - err = common.GORM.Where(guildID).First(cast).Error - if err != nil { - // Return default config if not found - if err == gorm.ErrRecordNotFound { - *cast = SoundboardConfig{ - GuildConfigModel: configstore.GuildConfigModel{ - GuildID: guildID, - }, - } - } else { - return err - } - } - - err = common.GORM.Where("guild_id = ?", guildID).Find(&cast.Sounds).Error - return err -} - -// SetGuildConfig saves the GuildConfig struct -func (p *Plugin) SetGuildConfig(ctx context.Context, conf configstore.GuildConfig) error { - return common.GORM.Save(conf).Error -} - -// SetIfLatest saves it only if the passedLatest time is the latest version -func (p *Plugin) SetIfLatest(ctx context.Context, conf configstore.GuildConfig) (updated bool, err error) { - err = p.SetGuildConfig(ctx, conf) - return true, err -} - func (p *Plugin) Name() string { return "Soundboard" } func RegisterPlugin() { + _, err := common.PQ.Exec(DBSchema) + if err != nil { + logrus.WithError(err).Error("failed initializing soundbaord database schema, not running...") + return + } + p := &Plugin{} common.RegisterPlugin(p) - configstore.RegisterConfig(p, &SoundboardConfig{}) - common.GORM.AutoMigrate(SoundboardConfig{}, SoundboardSound{}) - // Setup directories - err := os.MkdirAll("soundboard/queue", 0755) + err = os.MkdirAll("soundboard/queue", 0755) if err != nil { if !os.IsExist(err) { panic(err) @@ -67,16 +40,6 @@ func RegisterPlugin() { os.MkdirAll("soundboard/ready", 0755) } -type SoundboardConfig struct { - configstore.GuildConfigModel - - Sounds []*SoundboardSound `gorm:"ForeignKey:GuildID;AssociationForeignKey:GuildID"` -} - -func (sc *SoundboardConfig) GetName() string { - return "soundboard" -} - type TranscodingStatus int const ( @@ -106,16 +69,7 @@ func (s TranscodingStatus) String() string { return "Unknown" } -type SoundboardSound struct { - common.SmallModel - - GuildID int64 `gorm:"index"` - RequiredRole string `valid:"role,true"` - Name string `valid:",1,100"` - Status TranscodingStatus -} - -func (s *SoundboardSound) CanPlay(roles []int64) bool { +func CanPlaySound(s *models.SoundboardSound, roles []int64) bool { if s.RequiredRole == "" { return true } @@ -129,11 +83,11 @@ func (s *SoundboardSound) CanPlay(roles []int64) bool { return false } -func KeySoundLock(id uint) string { +func KeySoundLock(id int) string { return fmt.Sprintf("soundboard_soundlock:%d", id) } -func SoundFilePath(id uint, status TranscodingStatus) string { +func SoundFilePath(id int, status TranscodingStatus) string { if status == TranscodingStatusReady { return fmt.Sprintf("soundboard/ready/%d.dca", id) } @@ -153,3 +107,8 @@ func MaxSoundsForContext(ctx context.Context) int { return MaxGuildSounds } + +func GetSoundboardSounds(guildID int64, ctx context.Context) ([]*models.SoundboardSound, error) { + result, err := models.SoundboardSounds(qm.Where("guild_id=?", guildID)).AllG(ctx) + return result, err +} diff --git a/soundboard/sqlboiler.toml b/soundboard/sqlboiler.toml new file mode 100644 index 0000000000..e0e122b172 --- /dev/null +++ b/soundboard/sqlboiler.toml @@ -0,0 +1,10 @@ +add-global-variants="true" +no-hooks="true" + +[psql] +dbname="yagpdb" +host="localhost" +user="postgres" +pass="123" +sslmode="disable" +whitelist=["soundboard_sounds"] \ No newline at end of file diff --git a/soundboard/transcoder.go b/soundboard/transcoder.go index 5722d25718..41964e9dfb 100644 --- a/soundboard/transcoder.go +++ b/soundboard/transcoder.go @@ -1,10 +1,11 @@ package soundboard import ( + "context" "github.com/jonas747/dca" "github.com/jonas747/yagpdb/commands" "github.com/jonas747/yagpdb/common" - "github.com/jonas747/yagpdb/common/configstore" + "github.com/jonas747/yagpdb/soundboard/models" "github.com/sirupsen/logrus" "io" "io/ioutil" @@ -87,13 +88,13 @@ func handleQueueItem(item string) error { idStr = strings.SplitN(item, ".", 2)[0] } - parsedId, err := strconv.ParseInt(idStr, 10, 32) + parsedId, err := strconv.Atoi(idStr) if err != nil { return err } // lock it for max 10 minutes, after that something must've gone wrong - locked, err := common.TryLockRedisKey(KeySoundLock(uint(parsedId)), 10*60) + locked, err := common.TryLockRedisKey(KeySoundLock(parsedId), 10*60) if err != nil { return err } @@ -101,10 +102,9 @@ func handleQueueItem(item string) error { logrus.WithField("sound", parsedId).Warn("Sound is busy, handling it later") return nil } - defer common.UnlockRedisKey(KeySoundLock(uint(parsedId))) + defer common.UnlockRedisKey(KeySoundLock(parsedId)) - var sound SoundboardSound - err = common.GORM.Where(uint(parsedId)).First(&sound).Error + sound, err := models.FindSoundboardSoundG(context.Background(), parsedId) if err != nil { return err } @@ -112,7 +112,7 @@ func handleQueueItem(item string) error { logrus.WithField("sound", sound.ID).Info("Handling queued sound ", sound.Name) if !skipTranscode { - err = transcodeSound(&sound) + err = transcodeSound(sound) if err != nil { logrus.WithError(err).WithField("sound", sound.ID).Error("Failed transcoding sound") common.GORM.Model(&sound).Update("Status", TranscodingStatusFailedOther) @@ -121,7 +121,6 @@ func handleQueueItem(item string) error { common.GORM.Model(&sound).Update("Status", TranscodingStatusReady) } - configstore.InvalidateGuildCache(sound.GuildID, &SoundboardConfig{}) err = os.Remove(SoundFilePath(sound.ID, TranscodingStatusQueued)) } else { os.Rename(SoundFilePath(sound.ID, TranscodingStatusQueued)+".dca", SoundFilePath(sound.ID, TranscodingStatusReady)) @@ -129,14 +128,14 @@ func handleQueueItem(item string) error { return err } -func transcodeSound(sound *SoundboardSound) error { +func transcodeSound(sound *models.SoundboardSound) error { destFile, err := os.Create(SoundFilePath(sound.ID, TranscodingStatusReady)) if err != nil { return err } defer destFile.Close() - session, err := dca.EncodeFile(SoundFilePath(sound.ID, sound.Status), transcoderOptions) + session, err := dca.EncodeFile(SoundFilePath(sound.ID, TranscodingStatus(sound.Status)), transcoderOptions) if err != nil { return err } diff --git a/soundboard/web.go b/soundboard/web.go index 95b31431b5..e54f2021c2 100644 --- a/soundboard/web.go +++ b/soundboard/web.go @@ -1,11 +1,14 @@ package soundboard import ( - "errors" "fmt" "github.com/jonas747/yagpdb/common" - "github.com/jonas747/yagpdb/common/configstore" + "github.com/jonas747/yagpdb/soundboard/models" "github.com/jonas747/yagpdb/web" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/volatiletech/sqlboiler/boil" + "github.com/volatiletech/sqlboiler/queries/qm" "goji.io" "goji.io/pat" "html/template" @@ -17,6 +20,21 @@ import ( "strings" ) +type PostForm struct { + ID int + RequiredRole string `valid:"role,true"` + Name string `valid:",100"` +} + +func (pf *PostForm) ToDBModel() *models.SoundboardSound { + return &models.SoundboardSound{ + ID: pf.ID, + + Name: pf.Name, + RequiredRole: pf.RequiredRole, + } +} + func (p *Plugin) InitWeb() { tmplPath := "templates/plugins/soundboard.html" if common.Testing { @@ -37,23 +55,21 @@ func (p *Plugin) InitWeb() { cpMux.Handle(pat.Get("/"), getHandler) //cpMux.Handle(pat.Get(""), getHandler) - cpMux.Handle(pat.Post("/new"), web.ControllerPostHandler(HandleNew, getHandler, SoundboardSound{}, "Added a new sound to the soundboard")) - cpMux.Handle(pat.Post("/update"), web.ControllerPostHandler(HandleUpdate, getHandler, SoundboardSound{}, "Updated a soundboard sound")) - cpMux.Handle(pat.Post("/delete"), web.ControllerPostHandler(HandleDelete, getHandler, SoundboardSound{}, "Removed a sound from the soundboard")) + cpMux.Handle(pat.Post("/new"), web.ControllerPostHandler(HandleNew, getHandler, PostForm{}, "Added a new sound to the soundboard")) + cpMux.Handle(pat.Post("/update"), web.ControllerPostHandler(HandleUpdate, getHandler, PostForm{}, "Updated a soundboard sound")) + cpMux.Handle(pat.Post("/delete"), web.ControllerPostHandler(HandleDelete, getHandler, PostForm{}, "Removed a sound from the soundboard")) } func HandleGetCP(w http.ResponseWriter, r *http.Request) (web.TemplateData, error) { ctx := r.Context() g, tmpl := web.GetBaseCPContextData(ctx) - var config SoundboardConfig - err := configstore.Cached.GetGuildConfig(ctx, g.ID, &config) - + sounds, err := GetSoundboardSounds(g.ID, ctx) if err != nil { return tmpl, err } - tmpl["Config"] = config + tmpl["SoundboardSounds"] = sounds return tmpl, nil } @@ -75,50 +91,53 @@ func HandleNew(w http.ResponseWriter, r *http.Request) (web.TemplateData, error) } } - data := ctx.Value(common.ContextKeyParsedForm).(*SoundboardSound) - data.Status = TranscodingStatusQueued + data := ctx.Value(common.ContextKeyParsedForm).(*PostForm) + + dbModel := data.ToDBModel() + dbModel.Status = int(TranscodingStatusQueued) if isDCA { - data.Status = TranscodingStatusReady + dbModel.Status = int(TranscodingStatusReady) } - data.GuildID = g.ID + dbModel.GuildID = g.ID - count := 0 - err := common.GORM.Model(SoundboardSound{}).Where("guild_id = ? AND name = ?", g.ID, data.Name).Count(&count).Error + // check for name conflict + nameConflict, err := models.SoundboardSounds(qm.Where("guild_id=? AND name=?", g.ID, data.Name)).ExistsG(r.Context()) if err != nil { return tmpl, err } - if count > 0 { + if nameConflict { tmpl.AddAlerts(web.ErrorAlert("Name already used")) return tmpl, nil } - err = common.GORM.Model(SoundboardSound{}).Where("guild_id = ?", g.ID).Count(&count).Error + // check sound limit + count, err := models.SoundboardSounds(qm.Where("guild_id=?", g.ID)).CountG(r.Context()) if err != nil { return tmpl, err } - if count >= MaxSoundsForContext(ctx) { + if count >= int64(MaxSoundsForContext(ctx)) { tmpl.AddAlerts(web.ErrorAlert(fmt.Sprintf("Max %d sounds allowed (%d for premium servers)", MaxGuildSounds, MaxGuildSoundsPremium))) return tmpl, nil } - err = common.GORM.Create(data).Error + err = dbModel.InsertG(r.Context(), boil.Infer()) if err != nil { return tmpl, err } // Lock it - locked, err := common.TryLockRedisKey(KeySoundLock(data.ID), 60) + locked, err := common.TryLockRedisKey(KeySoundLock(dbModel.ID), 60) if err != nil || !locked { if !locked { tmpl.AddAlerts(web.ErrorAlert("Uh oh failed locking")) } return tmpl, err } - defer common.UnlockRedisKey(KeySoundLock(data.ID)) + defer common.UnlockRedisKey(KeySoundLock(dbModel.ID)) //logrus.Error("CREAte errror:", err) - fname := "soundboard/queue/" + strconv.Itoa(int(data.ID)) + fname := "soundboard/queue/" + strconv.Itoa(int(dbModel.ID)) if isDCA { fname += ".dca" } @@ -151,11 +170,10 @@ func HandleNew(w http.ResponseWriter, r *http.Request) (web.TemplateData, error) if tooBig { tmpl.AddAlerts(web.ErrorAlert("Max 10MB files allowed")) } - common.GORM.Delete(data) + dbModel.DeleteG(ctx) return tmpl, err } - configstore.InvalidateGuildCache(g.ID, &SoundboardConfig{}) return tmpl, err } @@ -195,26 +213,33 @@ func DownloadNewSoundFile(r io.Reader, w io.Writer, limit int) (tooBig bool, err func HandleUpdate(w http.ResponseWriter, r *http.Request) (web.TemplateData, error) { ctx := r.Context() g, tmpl := web.GetBaseCPContextData(ctx) - data := ctx.Value(common.ContextKeyParsedForm).(*SoundboardSound) - data.GuildID = g.ID + data := ctx.Value(common.ContextKeyParsedForm).(*PostForm) - count := 0 - common.GORM.Model(SoundboardSound{}).Where("guild_id = ? AND name = ? AND id != ?", g.ID, data.Name, data.ID).Count(&count) - if count > 0 { + dbModel, err := models.SoundboardSounds(qm.Where("guild_id = ? AND id = ?", g.ID, data.ID)).OneG(ctx) + if err != nil { + return tmpl.AddAlerts(web.ErrorAlert("Error retrieiving sound")), errors.Wrap(err, "unknown sound") + } + + nameConflict, err := models.SoundboardSounds(qm.Where("guild_id = ? AND name = ? AND id != ?", g.ID, data.Name, data.ID)).ExistsG(r.Context()) + if err != nil { + return tmpl, err + } + + if nameConflict { tmpl.AddAlerts(web.ErrorAlert("Name already used")) return tmpl, nil } - err := common.GORM.Model(data).Updates(map[string]interface{}{"name": data.Name, "required_role": data.RequiredRole}).Error - configstore.InvalidateGuildCache(g.ID, &SoundboardConfig{}) + dbModel.Name = data.Name + dbModel.RequiredRole = data.RequiredRole + _, err = dbModel.UpdateG(ctx, boil.Whitelist("name", "required_role", "updated_at")) return tmpl, err } func HandleDelete(w http.ResponseWriter, r *http.Request) (web.TemplateData, error) { ctx := r.Context() g, tmpl := web.GetBaseCPContextData(ctx) - data := ctx.Value(common.ContextKeyParsedForm).(*SoundboardSound) - data.GuildID = g.ID + data := ctx.Value(common.ContextKeyParsedForm).(*PostForm) locked, err := common.TryLockRedisKey(KeySoundLock(data.ID), 10) if err != nil { @@ -226,15 +251,14 @@ func HandleDelete(w http.ResponseWriter, r *http.Request) (web.TemplateData, err } defer common.UnlockRedisKey(KeySoundLock(data.ID)) - var storedSound SoundboardSound - err = common.GORM.Where("guild_id = ? AND id = ?", g.ID, data.ID).First(&storedSound).Error + storedSound, err := models.SoundboardSounds(qm.Where("guild_id = ? AND id = ?", g.ID, data.ID)).OneG(ctx) if err != nil { - return tmpl, nil + return tmpl, err } - switch storedSound.Status { + switch TranscodingStatus(storedSound.Status) { case TranscodingStatusQueued, TranscodingStatusReady: - err = os.Remove(SoundFilePath(data.ID, storedSound.Status)) + err = os.Remove(SoundFilePath(data.ID, TranscodingStatus(storedSound.Status))) case TranscodingStatusTranscoding: tmpl.AddAlerts(web.ErrorAlert("This sound is busy? try again in a minute and if its still busy contact support")) return tmpl, nil @@ -246,8 +270,6 @@ func HandleDelete(w http.ResponseWriter, r *http.Request) (web.TemplateData, err } } - err = common.GORM.Delete(storedSound).Error - configstore.InvalidateGuildCache(g.ID, &SoundboardConfig{}) - + _, err = storedSound.DeleteG(ctx) return tmpl, err }