From 23cdb2b2675c8555d4a906d09752a50b4b15e4f9 Mon Sep 17 00:00:00 2001 From: jonas747 Date: Sun, 26 May 2019 22:30:43 +0200 Subject: [PATCH] unify line endings to unix line endings --- bot/paginatedmessages/pageinatedmessage.go | 482 ++++---- common/config/config.go | 270 ++--- common/config/envsource.go | 36 +- common/config/singleton.go | 30 +- common/coreserverconf.go | 128 +- common/ensure64bit.go | 12 +- common/goutil.go | 130 +- common/multiratelimit/multiratelimit.go | 86 +- reddit/reddit_logo.go | 6 +- tickets/schema.go | 122 +- tickets/tickets_web.go | 236 ++-- verification/schema.go | 62 +- verification/verification_web.go | 600 +++++----- web/web_test.go | 1264 ++++++++++---------- 14 files changed, 1732 insertions(+), 1732 deletions(-) diff --git a/bot/paginatedmessages/pageinatedmessage.go b/bot/paginatedmessages/pageinatedmessage.go index 86ceb40031..22c8d06a49 100644 --- a/bot/paginatedmessages/pageinatedmessage.go +++ b/bot/paginatedmessages/pageinatedmessage.go @@ -1,241 +1,241 @@ -package paginatedmessages - -import ( - "github.com/jonas747/discordgo" - "github.com/jonas747/yagpdb/bot" - "github.com/jonas747/yagpdb/bot/eventsystem" - "github.com/jonas747/yagpdb/common" - "strconv" - "sync" - "time" -) - -var ( - logger = common.GetPluginLogger(&Plugin{}) - activePaginatedMessages []*PaginatedMessage - menusLock sync.Mutex -) - -type Plugin struct{} - -func (p *Plugin) PluginInfo() *common.PluginInfo { - return &common.PluginInfo{ - Name: "Paginated Messages", - SysName: "paginatedmessages", - Category: common.PluginCategoryMisc, - } -} - -func RegisterPlugin() { - common.RegisterPlugin(&Plugin{}) -} - -var _ bot.BotInitHandler = (*Plugin)(nil) - -func (p *Plugin) BotInit() { - - eventsystem.AddHandlerAsyncLast(func(evt *eventsystem.EventData) { - ra := evt.MessageReactionAdd() - - if ra.UserID == common.BotUser.ID { - return - } - - menusLock.Lock() - for _, v := range activePaginatedMessages { - if v.MessageID == ra.MessageID { - menusLock.Unlock() - v.HandleReactionAdd(ra) - return - } - } - menusLock.Unlock() - - }, eventsystem.EventMessageReactionAdd) -} - -func (p *Plugin) StopBot(wg *sync.WaitGroup) { - menusLock.Lock() - for _, v := range activePaginatedMessages { - go v.Stop() - } - menusLock.Unlock() - - wg.Done() -} - -type PaginatedMessage struct { - // immutable fields, safe to access without a lock, don't write to these, i dont see why you would need to either... - MessageID int64 - ChannelID int64 - GuildID int64 - - // mutable fields - CurrentPage int - MaxPage int - LastResponse *discordgo.MessageEmbed - Navigate func(p *PaginatedMessage, newPage int) (*discordgo.MessageEmbed, error) - - stopped bool - stopCh chan bool - lastUpdateTime time.Time - mu sync.Mutex -} - -const ( - EmojiNext = "➡" - EmojiPrev = "⬅" -) - -func CreatePaginatedMessage(guildID, channelID int64, initPage, maxPages int, pagerFunc func(p *PaginatedMessage, page int) (*discordgo.MessageEmbed, error)) (*PaginatedMessage, error) { - if initPage < 1 { - initPage = 1 - } - - pm := &PaginatedMessage{ - GuildID: guildID, - ChannelID: channelID, - - CurrentPage: initPage, - MaxPage: maxPages, - lastUpdateTime: time.Now(), - stopCh: make(chan bool), - Navigate: pagerFunc, - } - - embed, err := pagerFunc(pm, initPage) - if err != nil { - return nil, err - } - - footer := "Page " + strconv.Itoa(initPage) - if pm.MaxPage > 0 { - footer += "/" + strconv.Itoa(pm.MaxPage) - } - embed.Footer = &discordgo.MessageEmbedFooter{ - Text: footer, - } - embed.Timestamp = time.Now().Format(time.RFC3339) - - msg, err := common.BotSession.ChannelMessageSendEmbed(channelID, embed) - if err != nil { - return nil, err - } - pm.MessageID = msg.ID - - err = common.BotSession.MessageReactionAdd(channelID, msg.ID, EmojiPrev) - if err != nil { - return nil, err - } - err = common.BotSession.MessageReactionAdd(channelID, msg.ID, EmojiNext) - if err != nil { - return nil, err - } - - menusLock.Lock() - activePaginatedMessages = append(activePaginatedMessages, pm) - menusLock.Unlock() - - go pm.ticker() - return pm, nil -} - -func (p *PaginatedMessage) HandleReactionAdd(ra *discordgo.MessageReactionAdd) { - - pageMod := 0 - if ra.Emoji.Name == EmojiNext { - pageMod = 1 - } else if ra.Emoji.Name == EmojiPrev { - pageMod = -1 - } - - // remove the emoji to signal were handling it - err := common.BotSession.MessageReactionRemove(ra.ChannelID, ra.MessageID, ra.Emoji.APIName(), ra.UserID) - if err != nil { - logger.WithError(err).WithField("guild", p.GuildID).Error("failed removing reaction") - } - - p.mu.Lock() - defer p.mu.Unlock() - - if pageMod == 0 || (pageMod == -1 && p.CurrentPage <= 1) || - (p.MaxPage > 0 && pageMod == 1 && p.CurrentPage+pageMod > p.MaxPage) { - return - } - - newPage := p.CurrentPage + pageMod - newMsg, err := p.Navigate(p, newPage) - if err != nil { - logger.WithError(err).WithField("guild", p.GuildID).Error("failed getting new page") - return - } - - if newMsg == nil { - // No change... - return - } - p.LastResponse = newMsg - p.lastUpdateTime = time.Now() - - p.CurrentPage = newPage - footer := "Page " + strconv.Itoa(newPage) - if p.MaxPage > 0 { - footer += "/" + strconv.Itoa(p.MaxPage) - } - - newMsg.Footer = &discordgo.MessageEmbedFooter{ - Text: footer, - } - newMsg.Timestamp = time.Now().Format(time.RFC3339) - - _, err = common.BotSession.ChannelMessageEditEmbed(ra.ChannelID, ra.MessageID, newMsg) - if err != nil { - logger.WithError(err).WithField("guild", p.GuildID).Error("failed updating message") - } -} - -func (p *PaginatedMessage) ticker() { - t := time.NewTicker(time.Second * 5) - defer t.Stop() - -OUTER: - for { - select { - case <-t.C: - p.mu.Lock() - toRemove := time.Since(p.lastUpdateTime) > time.Minute - p.mu.Unlock() - if !toRemove { - continue OUTER - } - - case <-p.stopCh: - } - - // remove the reactions - common.BotSession.MessageReactionsRemoveAll(p.ChannelID, p.MessageID) - - // remove it - menusLock.Lock() - for i, v := range activePaginatedMessages { - if v == p { - activePaginatedMessages = append(activePaginatedMessages[:i], activePaginatedMessages[i+1:]...) - } - } - menusLock.Unlock() - return - } - -} - -func (p *PaginatedMessage) Stop() { - p.mu.Lock() - defer p.mu.Unlock() - - if p.stopped { - return - } - - p.stopped = true - close(p.stopCh) -} +package paginatedmessages + +import ( + "github.com/jonas747/discordgo" + "github.com/jonas747/yagpdb/bot" + "github.com/jonas747/yagpdb/bot/eventsystem" + "github.com/jonas747/yagpdb/common" + "strconv" + "sync" + "time" +) + +var ( + logger = common.GetPluginLogger(&Plugin{}) + activePaginatedMessages []*PaginatedMessage + menusLock sync.Mutex +) + +type Plugin struct{} + +func (p *Plugin) PluginInfo() *common.PluginInfo { + return &common.PluginInfo{ + Name: "Paginated Messages", + SysName: "paginatedmessages", + Category: common.PluginCategoryMisc, + } +} + +func RegisterPlugin() { + common.RegisterPlugin(&Plugin{}) +} + +var _ bot.BotInitHandler = (*Plugin)(nil) + +func (p *Plugin) BotInit() { + + eventsystem.AddHandlerAsyncLast(func(evt *eventsystem.EventData) { + ra := evt.MessageReactionAdd() + + if ra.UserID == common.BotUser.ID { + return + } + + menusLock.Lock() + for _, v := range activePaginatedMessages { + if v.MessageID == ra.MessageID { + menusLock.Unlock() + v.HandleReactionAdd(ra) + return + } + } + menusLock.Unlock() + + }, eventsystem.EventMessageReactionAdd) +} + +func (p *Plugin) StopBot(wg *sync.WaitGroup) { + menusLock.Lock() + for _, v := range activePaginatedMessages { + go v.Stop() + } + menusLock.Unlock() + + wg.Done() +} + +type PaginatedMessage struct { + // immutable fields, safe to access without a lock, don't write to these, i dont see why you would need to either... + MessageID int64 + ChannelID int64 + GuildID int64 + + // mutable fields + CurrentPage int + MaxPage int + LastResponse *discordgo.MessageEmbed + Navigate func(p *PaginatedMessage, newPage int) (*discordgo.MessageEmbed, error) + + stopped bool + stopCh chan bool + lastUpdateTime time.Time + mu sync.Mutex +} + +const ( + EmojiNext = "➡" + EmojiPrev = "⬅" +) + +func CreatePaginatedMessage(guildID, channelID int64, initPage, maxPages int, pagerFunc func(p *PaginatedMessage, page int) (*discordgo.MessageEmbed, error)) (*PaginatedMessage, error) { + if initPage < 1 { + initPage = 1 + } + + pm := &PaginatedMessage{ + GuildID: guildID, + ChannelID: channelID, + + CurrentPage: initPage, + MaxPage: maxPages, + lastUpdateTime: time.Now(), + stopCh: make(chan bool), + Navigate: pagerFunc, + } + + embed, err := pagerFunc(pm, initPage) + if err != nil { + return nil, err + } + + footer := "Page " + strconv.Itoa(initPage) + if pm.MaxPage > 0 { + footer += "/" + strconv.Itoa(pm.MaxPage) + } + embed.Footer = &discordgo.MessageEmbedFooter{ + Text: footer, + } + embed.Timestamp = time.Now().Format(time.RFC3339) + + msg, err := common.BotSession.ChannelMessageSendEmbed(channelID, embed) + if err != nil { + return nil, err + } + pm.MessageID = msg.ID + + err = common.BotSession.MessageReactionAdd(channelID, msg.ID, EmojiPrev) + if err != nil { + return nil, err + } + err = common.BotSession.MessageReactionAdd(channelID, msg.ID, EmojiNext) + if err != nil { + return nil, err + } + + menusLock.Lock() + activePaginatedMessages = append(activePaginatedMessages, pm) + menusLock.Unlock() + + go pm.ticker() + return pm, nil +} + +func (p *PaginatedMessage) HandleReactionAdd(ra *discordgo.MessageReactionAdd) { + + pageMod := 0 + if ra.Emoji.Name == EmojiNext { + pageMod = 1 + } else if ra.Emoji.Name == EmojiPrev { + pageMod = -1 + } + + // remove the emoji to signal were handling it + err := common.BotSession.MessageReactionRemove(ra.ChannelID, ra.MessageID, ra.Emoji.APIName(), ra.UserID) + if err != nil { + logger.WithError(err).WithField("guild", p.GuildID).Error("failed removing reaction") + } + + p.mu.Lock() + defer p.mu.Unlock() + + if pageMod == 0 || (pageMod == -1 && p.CurrentPage <= 1) || + (p.MaxPage > 0 && pageMod == 1 && p.CurrentPage+pageMod > p.MaxPage) { + return + } + + newPage := p.CurrentPage + pageMod + newMsg, err := p.Navigate(p, newPage) + if err != nil { + logger.WithError(err).WithField("guild", p.GuildID).Error("failed getting new page") + return + } + + if newMsg == nil { + // No change... + return + } + p.LastResponse = newMsg + p.lastUpdateTime = time.Now() + + p.CurrentPage = newPage + footer := "Page " + strconv.Itoa(newPage) + if p.MaxPage > 0 { + footer += "/" + strconv.Itoa(p.MaxPage) + } + + newMsg.Footer = &discordgo.MessageEmbedFooter{ + Text: footer, + } + newMsg.Timestamp = time.Now().Format(time.RFC3339) + + _, err = common.BotSession.ChannelMessageEditEmbed(ra.ChannelID, ra.MessageID, newMsg) + if err != nil { + logger.WithError(err).WithField("guild", p.GuildID).Error("failed updating message") + } +} + +func (p *PaginatedMessage) ticker() { + t := time.NewTicker(time.Second * 5) + defer t.Stop() + +OUTER: + for { + select { + case <-t.C: + p.mu.Lock() + toRemove := time.Since(p.lastUpdateTime) > time.Minute + p.mu.Unlock() + if !toRemove { + continue OUTER + } + + case <-p.stopCh: + } + + // remove the reactions + common.BotSession.MessageReactionsRemoveAll(p.ChannelID, p.MessageID) + + // remove it + menusLock.Lock() + for i, v := range activePaginatedMessages { + if v == p { + activePaginatedMessages = append(activePaginatedMessages[:i], activePaginatedMessages[i+1:]...) + } + } + menusLock.Unlock() + return + } + +} + +func (p *PaginatedMessage) Stop() { + p.mu.Lock() + defer p.mu.Unlock() + + if p.stopped { + return + } + + p.stopped = true + close(p.stopCh) +} diff --git a/common/config/config.go b/common/config/config.go index 1db99b8ced..0c5751cedf 100644 --- a/common/config/config.go +++ b/common/config/config.go @@ -1,135 +1,135 @@ -package config - -import ( - "strconv" - "strings" -) - -type ConfigSource interface { - GetValue(key string) interface{} -} - -type ConfigOption struct { - Name string - Description string - DefaultValue interface{} - LoadedValue interface{} - Manager *ConfigManager -} - -func (opt *ConfigOption) LoadValue() { - newVal := opt.DefaultValue - - for i := len(opt.Manager.sources) - 1; i >= 0; i-- { - source := opt.Manager.sources[i] - - v := source.GetValue(opt.Name) - if v != nil { - newVal = v - break - } - } - - // parse ahead of time - if opt.DefaultValue != nil { - if _, ok := opt.DefaultValue.(int); ok { - newVal = interface{}(intVal(newVal)) - } else if _, ok := opt.DefaultValue.(bool); ok { - newVal = interface{}(boolVal(newVal)) - } - } - - opt.LoadedValue = newVal -} - -func (opt *ConfigOption) GetString() string { - return strVal(opt.LoadedValue) -} - -func (opt *ConfigOption) GetInt() int { - return intVal(opt.LoadedValue) -} - -func (opt *ConfigOption) GetBool() bool { - return boolVal(opt.LoadedValue) -} - -type ConfigManager struct { - sources []ConfigSource - options map[string]*ConfigOption -} - -func NewConfigManager() *ConfigManager { - return &ConfigManager{ - options: make(map[string]*ConfigOption), - } -} - -func (c *ConfigManager) AddSource(source ConfigSource) { - c.sources = append(c.sources, source) -} - -func (c *ConfigManager) RegisterOption(name, desc string, defaultValue interface{}) *ConfigOption { - opt := &ConfigOption{ - Name: name, - Description: desc, - DefaultValue: defaultValue, - Manager: c, - } - - c.options[name] = opt - return opt -} - -func (c *ConfigManager) Load() { - for _, v := range c.options { - v.LoadValue() - } -} - -func strVal(i interface{}) string { - switch t := i.(type) { - case string: - return t - case int: - return strconv.FormatInt(int64(t), 10) - case Stringer: - return t.String() - } - - return "" -} - -type Stringer interface { - String() string -} - -func intVal(i interface{}) int { - switch t := i.(type) { - case string: - n, _ := strconv.ParseInt(t, 10, 64) - return int(n) - case int: - return t - } - - return 0 -} - -func boolVal(i interface{}) bool { - switch t := i.(type) { - case string: - lower := strings.ToLower(strings.TrimSpace(t)) - if lower == "true" || lower == "yes" || lower == "on" || lower == "enabled" || lower == "1" { - return true - } - - return false - case int: - return t > 0 - case bool: - return t - } - - return false -} +package config + +import ( + "strconv" + "strings" +) + +type ConfigSource interface { + GetValue(key string) interface{} +} + +type ConfigOption struct { + Name string + Description string + DefaultValue interface{} + LoadedValue interface{} + Manager *ConfigManager +} + +func (opt *ConfigOption) LoadValue() { + newVal := opt.DefaultValue + + for i := len(opt.Manager.sources) - 1; i >= 0; i-- { + source := opt.Manager.sources[i] + + v := source.GetValue(opt.Name) + if v != nil { + newVal = v + break + } + } + + // parse ahead of time + if opt.DefaultValue != nil { + if _, ok := opt.DefaultValue.(int); ok { + newVal = interface{}(intVal(newVal)) + } else if _, ok := opt.DefaultValue.(bool); ok { + newVal = interface{}(boolVal(newVal)) + } + } + + opt.LoadedValue = newVal +} + +func (opt *ConfigOption) GetString() string { + return strVal(opt.LoadedValue) +} + +func (opt *ConfigOption) GetInt() int { + return intVal(opt.LoadedValue) +} + +func (opt *ConfigOption) GetBool() bool { + return boolVal(opt.LoadedValue) +} + +type ConfigManager struct { + sources []ConfigSource + options map[string]*ConfigOption +} + +func NewConfigManager() *ConfigManager { + return &ConfigManager{ + options: make(map[string]*ConfigOption), + } +} + +func (c *ConfigManager) AddSource(source ConfigSource) { + c.sources = append(c.sources, source) +} + +func (c *ConfigManager) RegisterOption(name, desc string, defaultValue interface{}) *ConfigOption { + opt := &ConfigOption{ + Name: name, + Description: desc, + DefaultValue: defaultValue, + Manager: c, + } + + c.options[name] = opt + return opt +} + +func (c *ConfigManager) Load() { + for _, v := range c.options { + v.LoadValue() + } +} + +func strVal(i interface{}) string { + switch t := i.(type) { + case string: + return t + case int: + return strconv.FormatInt(int64(t), 10) + case Stringer: + return t.String() + } + + return "" +} + +type Stringer interface { + String() string +} + +func intVal(i interface{}) int { + switch t := i.(type) { + case string: + n, _ := strconv.ParseInt(t, 10, 64) + return int(n) + case int: + return t + } + + return 0 +} + +func boolVal(i interface{}) bool { + switch t := i.(type) { + case string: + lower := strings.ToLower(strings.TrimSpace(t)) + if lower == "true" || lower == "yes" || lower == "on" || lower == "enabled" || lower == "1" { + return true + } + + return false + case int: + return t > 0 + case bool: + return t + } + + return false +} diff --git a/common/config/envsource.go b/common/config/envsource.go index b3848cc02b..319f011ad7 100644 --- a/common/config/envsource.go +++ b/common/config/envsource.go @@ -1,18 +1,18 @@ -package config - -import ( - "os" - "strings" -) - -type EnvSource struct{} - -func (e *EnvSource) GetValue(key string) interface{} { - properKey := strings.ToUpper(key) - properKey = strings.Replace(properKey, ".", "_", -1) - v := os.Getenv(properKey) - if v == "" { - return nil - } - return v -} +package config + +import ( + "os" + "strings" +) + +type EnvSource struct{} + +func (e *EnvSource) GetValue(key string) interface{} { + properKey := strings.ToUpper(key) + properKey = strings.Replace(properKey, ".", "_", -1) + v := os.Getenv(properKey) + if v == "" { + return nil + } + return v +} diff --git a/common/config/singleton.go b/common/config/singleton.go index 3d26a4918c..148008506e 100644 --- a/common/config/singleton.go +++ b/common/config/singleton.go @@ -1,15 +1,15 @@ -package config - -var singleton = NewConfigManager() - -func AddSource(source ConfigSource) { - singleton.AddSource(source) -} - -func RegisterOption(name, desc string, defaultValue interface{}) *ConfigOption { - return singleton.RegisterOption(name, desc, defaultValue) -} - -func Load() { - singleton.Load() -} +package config + +var singleton = NewConfigManager() + +func AddSource(source ConfigSource) { + singleton.AddSource(source) +} + +func RegisterOption(name, desc string, defaultValue interface{}) *ConfigOption { + return singleton.RegisterOption(name, desc, defaultValue) +} + +func Load() { + singleton.Load() +} diff --git a/common/coreserverconf.go b/common/coreserverconf.go index dd62a3b898..61b9fb87d0 100644 --- a/common/coreserverconf.go +++ b/common/coreserverconf.go @@ -1,64 +1,64 @@ -package common - -import ( - "context" - "database/sql" - "github.com/jonas747/yagpdb/common/models" - "github.com/karlseguin/rcache" - "github.com/volatiletech/sqlboiler/boil" - "time" -) - -const CoreServerConfDBSchema = ` -CREATE TABLE IF NOT EXISTS core_configs ( - guild_id BIGINT PRIMARY KEY, - - allowed_read_only_roles BIGINT[], - allowed_write_roles BIGINT[], - - allow_all_members_read_only BOOLEAN NOT NULL, - allow_non_members_read_only BOOLEAN NOT NULL -) - -` - -var CoreServerConfigCache = rcache.NewInt(coreServerConfigCacheFetcher, time.Minute) - -func GetCoreServerConfCached(guildID int64) *models.CoreConfig { - return CoreServerConfigCache.Get(int(guildID)).(*models.CoreConfig) -} - -func coreServerConfigCacheFetcher(key int) interface{} { - conf, err := models.FindCoreConfigG(context.Background(), int64(key)) - if err != nil && err != sql.ErrNoRows { - logger.WithError(err).WithField("guild", key).Error("failed fetching core server config") - } - - if conf == nil { - conf = &models.CoreConfig{ - GuildID: int64(key), - } - } - - return conf -} - -func ContextCoreConf(ctx context.Context) *models.CoreConfig { - v := ctx.Value(ContextKeyCoreConfig) - if v == nil { - return nil - } - - return v.(*models.CoreConfig) -} - -func CoreConfigSave(ctx context.Context, m *models.CoreConfig) error { - err := m.UpsertG(ctx, true, []string{"guild_id"}, boil.Infer(), boil.Infer()) - if err != nil { - return err - } - - CoreServerConfigCache.Delete(int(m.GuildID)) - - return nil -} +package common + +import ( + "context" + "database/sql" + "github.com/jonas747/yagpdb/common/models" + "github.com/karlseguin/rcache" + "github.com/volatiletech/sqlboiler/boil" + "time" +) + +const CoreServerConfDBSchema = ` +CREATE TABLE IF NOT EXISTS core_configs ( + guild_id BIGINT PRIMARY KEY, + + allowed_read_only_roles BIGINT[], + allowed_write_roles BIGINT[], + + allow_all_members_read_only BOOLEAN NOT NULL, + allow_non_members_read_only BOOLEAN NOT NULL +) + +` + +var CoreServerConfigCache = rcache.NewInt(coreServerConfigCacheFetcher, time.Minute) + +func GetCoreServerConfCached(guildID int64) *models.CoreConfig { + return CoreServerConfigCache.Get(int(guildID)).(*models.CoreConfig) +} + +func coreServerConfigCacheFetcher(key int) interface{} { + conf, err := models.FindCoreConfigG(context.Background(), int64(key)) + if err != nil && err != sql.ErrNoRows { + logger.WithError(err).WithField("guild", key).Error("failed fetching core server config") + } + + if conf == nil { + conf = &models.CoreConfig{ + GuildID: int64(key), + } + } + + return conf +} + +func ContextCoreConf(ctx context.Context) *models.CoreConfig { + v := ctx.Value(ContextKeyCoreConfig) + if v == nil { + return nil + } + + return v.(*models.CoreConfig) +} + +func CoreConfigSave(ctx context.Context, m *models.CoreConfig) error { + err := m.UpsertG(ctx, true, []string{"guild_id"}, boil.Infer(), boil.Infer()) + if err != nil { + return err + } + + CoreServerConfigCache.Delete(int(m.GuildID)) + + return nil +} diff --git a/common/ensure64bit.go b/common/ensure64bit.go index 2c548113ec..cfd4c4416b 100644 --- a/common/ensure64bit.go +++ b/common/ensure64bit.go @@ -1,6 +1,6 @@ -// +build amd64 arm64 arm64be ppc64 ppc64le mips64 mips64le s390x sparc64 - -package common - -// sadly we assume int is 64bits in length, so we don't support 32bit builds because of that. -func ensure64bit() {} +// +build amd64 arm64 arm64be ppc64 ppc64le mips64 mips64le s390x sparc64 + +package common + +// sadly we assume int is 64bits in length, so we don't support 32bit builds because of that. +func ensure64bit() {} diff --git a/common/goutil.go b/common/goutil.go index 022f03caae..ad8939db8f 100644 --- a/common/goutil.go +++ b/common/goutil.go @@ -1,65 +1,65 @@ -package common - -import ( - "strings" -) - -func ContainsStringSlice(strs []string, search string) bool { - for _, v := range strs { - if v == search { - return true - } - } - - return false -} - -func ContainsStringSliceFold(strs []string, search string) bool { - for _, v := range strs { - if strings.EqualFold(v, search) { - return true - } - } - - return false -} - -func ContainsInt64Slice(slice []int64, search int64) bool { - for _, v := range slice { - if v == search { - return true - } - } - - return false -} - -// ContainsInt64SliceOneOf returns true if slice contains one of search -func ContainsInt64SliceOneOf(slice []int64, search []int64) bool { - for _, v := range search { - if ContainsInt64Slice(slice, v) { - return true - } - } - - return false -} - -func ContainsIntSlice(slice []int, search int) bool { - for _, v := range slice { - if v == search { - return true - } - } - - return false -} - -func IsNumber(v interface{}) bool { - switch v.(type) { - case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64: - return true - } - - return false -} +package common + +import ( + "strings" +) + +func ContainsStringSlice(strs []string, search string) bool { + for _, v := range strs { + if v == search { + return true + } + } + + return false +} + +func ContainsStringSliceFold(strs []string, search string) bool { + for _, v := range strs { + if strings.EqualFold(v, search) { + return true + } + } + + return false +} + +func ContainsInt64Slice(slice []int64, search int64) bool { + for _, v := range slice { + if v == search { + return true + } + } + + return false +} + +// ContainsInt64SliceOneOf returns true if slice contains one of search +func ContainsInt64SliceOneOf(slice []int64, search []int64) bool { + for _, v := range search { + if ContainsInt64Slice(slice, v) { + return true + } + } + + return false +} + +func ContainsIntSlice(slice []int, search int) bool { + for _, v := range slice { + if v == search { + return true + } + } + + return false +} + +func IsNumber(v interface{}) bool { + switch v.(type) { + case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64: + return true + } + + return false +} diff --git a/common/multiratelimit/multiratelimit.go b/common/multiratelimit/multiratelimit.go index 79f94ff29b..55a7f57c0c 100644 --- a/common/multiratelimit/multiratelimit.go +++ b/common/multiratelimit/multiratelimit.go @@ -1,43 +1,43 @@ -package multiratelimit - -import ( - "golang.org/x/time/rate" - "sync" - "time" -) - -type MultiRatelimiter struct { - mu sync.Mutex - limiters map[interface{}]*rate.Limiter - - maxPerSecond float64 - maxBurst int -} - -func NewMultiRatelimiter(maxPerSecond float64, maxBurst int) *MultiRatelimiter { - multiLimiter := &MultiRatelimiter{ - limiters: make(map[interface{}]*rate.Limiter), - - maxPerSecond: maxPerSecond, - maxBurst: maxBurst, - } - - return multiLimiter -} - -func (multi *MultiRatelimiter) findCreateLimiter(key interface{}) *rate.Limiter { - multi.mu.Lock() - defer multi.mu.Unlock() - - if current, ok := multi.limiters[key]; ok { - return current - } - - // not found, create it - multi.limiters[key] = rate.NewLimiter(rate.Limit(multi.maxPerSecond), multi.maxBurst) - return multi.limiters[key] -} - -func (multi *MultiRatelimiter) AllowN(key interface{}, now time.Time, n int) bool { - return multi.findCreateLimiter(key).AllowN(now, n) -} +package multiratelimit + +import ( + "golang.org/x/time/rate" + "sync" + "time" +) + +type MultiRatelimiter struct { + mu sync.Mutex + limiters map[interface{}]*rate.Limiter + + maxPerSecond float64 + maxBurst int +} + +func NewMultiRatelimiter(maxPerSecond float64, maxBurst int) *MultiRatelimiter { + multiLimiter := &MultiRatelimiter{ + limiters: make(map[interface{}]*rate.Limiter), + + maxPerSecond: maxPerSecond, + maxBurst: maxBurst, + } + + return multiLimiter +} + +func (multi *MultiRatelimiter) findCreateLimiter(key interface{}) *rate.Limiter { + multi.mu.Lock() + defer multi.mu.Unlock() + + if current, ok := multi.limiters[key]; ok { + return current + } + + // not found, create it + multi.limiters[key] = rate.NewLimiter(rate.Limit(multi.maxPerSecond), multi.maxBurst) + return multi.limiters[key] +} + +func (multi *MultiRatelimiter) AllowN(key interface{}, now time.Time, n int) bool { + return multi.findCreateLimiter(key).AllowN(now, n) +} diff --git a/reddit/reddit_logo.go b/reddit/reddit_logo.go index 87c283f82d..fef5c7da6b 100644 --- a/reddit/reddit_logo.go +++ b/reddit/reddit_logo.go @@ -1,3 +1,3 @@ -package reddit - -const RedditLogoPNGB64 = `data:image/png;base64,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` +package reddit + +const RedditLogoPNGB64 = `data:image/png;base64,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` diff --git a/tickets/schema.go b/tickets/schema.go index 7b05502791..0f5d2b7793 100644 --- a/tickets/schema.go +++ b/tickets/schema.go @@ -1,61 +1,61 @@ -package tickets - -const DBSchema = ` -CREATE TABLE IF NOT EXISTS ticket_configs ( - guild_id BIGINT PRIMARY KEY, - - enabled BOOLEAN NOT NULL, - - ticket_open_msg TEXT NOT NULL, - - tickets_channel_category BIGINT NOT NULL, - status_channel BIGINT NOT NULL, - tickets_transcripts_channel BIGINT NOT NULL, - download_attachments BOOLEAN NOT NULL, - tickets_use_txt_transcripts BOOLEAN NOT NULL, - - mod_roles BIGINT[], - admin_roles BIGINT[] -); - -CREATE TABLE IF NOT EXISTS tickets ( - guild_id BIGINT NOT NULL, - local_id BIGINT NOT NULL, - - channel_id BIGINT NOT NULL, - - title TEXT NOT NULL, - - created_at TIMESTAMP WITH TIME ZONE NOT NULL, - closed_at TIMESTAMP WITH TIME ZONE, - - logs_id BIGINT NOT NULL, - - author_id BIGINT NOT NULL, - author_username_discrim TEXT NOT NULL, - - PRIMARY KEY(guild_id, local_id) -); - -CREATE INDEX IF NOT EXISTS tickets_guild_id_channel_id_idx ON tickets(guild_id, channel_id); - -CREATE TABLE IF NOT EXISTS ticket_participants ( - ticket_guild_id BIGINT NOT NULL, - ticket_local_id BIGINT NOT NULL, - - user_id BIGINT NOT NULL, - username TEXT NOT NULL, - discrim TEXT NOT NULL, - - is_staff BOOLEAN NOT NULL, - - -- This is bugged in sqlboiler, sooooo don't use it for now i guess - -- FOREIGN KEY (ticket_guild_id, ticket_local_id) REFERENCES tickets(guild_id, local_id) ON DELETE CASCADE, - - PRIMARY KEY(ticket_guild_id, ticket_local_id, user_id) - - -); - -CREATE INDEX IF NOT EXISTS ticket_participants_ticket_local_id_idx ON ticket_participants(ticket_guild_id, ticket_local_id); -` +package tickets + +const DBSchema = ` +CREATE TABLE IF NOT EXISTS ticket_configs ( + guild_id BIGINT PRIMARY KEY, + + enabled BOOLEAN NOT NULL, + + ticket_open_msg TEXT NOT NULL, + + tickets_channel_category BIGINT NOT NULL, + status_channel BIGINT NOT NULL, + tickets_transcripts_channel BIGINT NOT NULL, + download_attachments BOOLEAN NOT NULL, + tickets_use_txt_transcripts BOOLEAN NOT NULL, + + mod_roles BIGINT[], + admin_roles BIGINT[] +); + +CREATE TABLE IF NOT EXISTS tickets ( + guild_id BIGINT NOT NULL, + local_id BIGINT NOT NULL, + + channel_id BIGINT NOT NULL, + + title TEXT NOT NULL, + + created_at TIMESTAMP WITH TIME ZONE NOT NULL, + closed_at TIMESTAMP WITH TIME ZONE, + + logs_id BIGINT NOT NULL, + + author_id BIGINT NOT NULL, + author_username_discrim TEXT NOT NULL, + + PRIMARY KEY(guild_id, local_id) +); + +CREATE INDEX IF NOT EXISTS tickets_guild_id_channel_id_idx ON tickets(guild_id, channel_id); + +CREATE TABLE IF NOT EXISTS ticket_participants ( + ticket_guild_id BIGINT NOT NULL, + ticket_local_id BIGINT NOT NULL, + + user_id BIGINT NOT NULL, + username TEXT NOT NULL, + discrim TEXT NOT NULL, + + is_staff BOOLEAN NOT NULL, + + -- This is bugged in sqlboiler, sooooo don't use it for now i guess + -- FOREIGN KEY (ticket_guild_id, ticket_local_id) REFERENCES tickets(guild_id, local_id) ON DELETE CASCADE, + + PRIMARY KEY(ticket_guild_id, ticket_local_id, user_id) + + +); + +CREATE INDEX IF NOT EXISTS ticket_participants_ticket_local_id_idx ON ticket_participants(ticket_guild_id, ticket_local_id); +` diff --git a/tickets/tickets_web.go b/tickets/tickets_web.go index 386aaad22d..f4ac79c9aa 100644 --- a/tickets/tickets_web.go +++ b/tickets/tickets_web.go @@ -1,118 +1,118 @@ -package tickets - -import ( - "database/sql" - "fmt" - "github.com/jonas747/yagpdb/common" - "github.com/jonas747/yagpdb/tickets/models" - "github.com/jonas747/yagpdb/web" - "github.com/volatiletech/sqlboiler/boil" - "goji.io/pat" - "html/template" - "net/http" -) - -type FormData struct { - GuildID int64 - Enabled bool - TicketsChannelCategory int64 - TicketsTranscriptsChannel int64 - StatusChannel int64 - TicketsUseTXTTranscripts bool - DownloadAttachments bool - ModRoles []int64 `valid:"role"` - AdminRoles []int64 `valid:"role"` - TicketOpenMSG string `valid:"template,10000"` -} - -func (p *Plugin) InitWeb() { - web.LoadHTMLTemplate("../../tickets/assets/tickets_control_panel.html", "templates/plugins/tickets_control_panel.html") - - web.AddSidebarItem(web.SidebarCategoryTools, &web.SidebarItem{ - Name: "Ticket System", - URL: "tickets/settings", - }) - - getHandler := web.ControllerHandler(p.handleGetSettings, "cp_tickets_settings") - postHandler := web.ControllerPostHandler(p.handlePostSettings, getHandler, FormData{}, "Updated ticket settings") - - web.CPMux.Handle(pat.Get("/tickets/settings"), web.RequireGuildChannelsMiddleware(getHandler)) - web.CPMux.Handle(pat.Get("/tickets/settings/"), web.RequireGuildChannelsMiddleware(getHandler)) - - web.CPMux.Handle(pat.Post("/tickets/settings"), web.RequireGuildChannelsMiddleware(postHandler)) -} - -func (p *Plugin) handleGetSettings(w http.ResponseWriter, r *http.Request) (web.TemplateData, error) { - ctx := r.Context() - activeGuild, templateData := web.GetBaseCPContextData(ctx) - - settings, err := models.FindTicketConfigG(ctx, activeGuild.ID) - if err != nil { - if err != sql.ErrNoRows { - return templateData, err - } - - // return standard config - settings = &models.TicketConfig{} - } - - templateData["DefaultTicketMessage"] = DefaultTicketMsg - templateData["PluginSettings"] = settings - - return templateData, nil -} - -func (p *Plugin) handlePostSettings(w http.ResponseWriter, r *http.Request) (web.TemplateData, error) { - ctx := r.Context() - activeGuild, templateData := web.GetBaseCPContextData(ctx) - - formConfig := ctx.Value(common.ContextKeyParsedForm).(*FormData) - - model := &models.TicketConfig{ - GuildID: activeGuild.ID, - Enabled: formConfig.Enabled, - TicketsChannelCategory: formConfig.TicketsChannelCategory, - TicketsTranscriptsChannel: formConfig.TicketsTranscriptsChannel, - StatusChannel: formConfig.StatusChannel, - TicketsUseTXTTranscripts: formConfig.TicketsUseTXTTranscripts, - DownloadAttachments: formConfig.DownloadAttachments, - ModRoles: formConfig.ModRoles, - AdminRoles: formConfig.AdminRoles, - TicketOpenMSG: formConfig.TicketOpenMSG, - } - - err := model.UpsertG(ctx, true, []string{"guild_id"}, boil.Infer(), boil.Infer()) - return templateData, err -} - -var _ web.PluginWithServerHomeWidget = (*Plugin)(nil) - -func (p *Plugin) LoadServerHomeWidget(w http.ResponseWriter, r *http.Request) (web.TemplateData, error) { - activeGuild, templateData := web.GetBaseCPContextData(r.Context()) - - settings, err := models.FindTicketConfigG(r.Context(), activeGuild.ID) - if err != nil && err != sql.ErrNoRows { - return templateData, err - } - - enabled := false - if settings != nil { - enabled = true - } - - templateData["WidgetTitle"] = "Tickets" - templateData["SettingsPath"] = "/tickets/settings" - if enabled { - templateData["WidgetEnabled"] = true - } else { - templateData["WidgetDisabled"] = true - } - - const format = `` - - templateData["WidgetBody"] = template.HTML(fmt.Sprintf(format, web.EnabledDisabledSpanStatus(enabled))) - - return templateData, nil -} +package tickets + +import ( + "database/sql" + "fmt" + "github.com/jonas747/yagpdb/common" + "github.com/jonas747/yagpdb/tickets/models" + "github.com/jonas747/yagpdb/web" + "github.com/volatiletech/sqlboiler/boil" + "goji.io/pat" + "html/template" + "net/http" +) + +type FormData struct { + GuildID int64 + Enabled bool + TicketsChannelCategory int64 + TicketsTranscriptsChannel int64 + StatusChannel int64 + TicketsUseTXTTranscripts bool + DownloadAttachments bool + ModRoles []int64 `valid:"role"` + AdminRoles []int64 `valid:"role"` + TicketOpenMSG string `valid:"template,10000"` +} + +func (p *Plugin) InitWeb() { + web.LoadHTMLTemplate("../../tickets/assets/tickets_control_panel.html", "templates/plugins/tickets_control_panel.html") + + web.AddSidebarItem(web.SidebarCategoryTools, &web.SidebarItem{ + Name: "Ticket System", + URL: "tickets/settings", + }) + + getHandler := web.ControllerHandler(p.handleGetSettings, "cp_tickets_settings") + postHandler := web.ControllerPostHandler(p.handlePostSettings, getHandler, FormData{}, "Updated ticket settings") + + web.CPMux.Handle(pat.Get("/tickets/settings"), web.RequireGuildChannelsMiddleware(getHandler)) + web.CPMux.Handle(pat.Get("/tickets/settings/"), web.RequireGuildChannelsMiddleware(getHandler)) + + web.CPMux.Handle(pat.Post("/tickets/settings"), web.RequireGuildChannelsMiddleware(postHandler)) +} + +func (p *Plugin) handleGetSettings(w http.ResponseWriter, r *http.Request) (web.TemplateData, error) { + ctx := r.Context() + activeGuild, templateData := web.GetBaseCPContextData(ctx) + + settings, err := models.FindTicketConfigG(ctx, activeGuild.ID) + if err != nil { + if err != sql.ErrNoRows { + return templateData, err + } + + // return standard config + settings = &models.TicketConfig{} + } + + templateData["DefaultTicketMessage"] = DefaultTicketMsg + templateData["PluginSettings"] = settings + + return templateData, nil +} + +func (p *Plugin) handlePostSettings(w http.ResponseWriter, r *http.Request) (web.TemplateData, error) { + ctx := r.Context() + activeGuild, templateData := web.GetBaseCPContextData(ctx) + + formConfig := ctx.Value(common.ContextKeyParsedForm).(*FormData) + + model := &models.TicketConfig{ + GuildID: activeGuild.ID, + Enabled: formConfig.Enabled, + TicketsChannelCategory: formConfig.TicketsChannelCategory, + TicketsTranscriptsChannel: formConfig.TicketsTranscriptsChannel, + StatusChannel: formConfig.StatusChannel, + TicketsUseTXTTranscripts: formConfig.TicketsUseTXTTranscripts, + DownloadAttachments: formConfig.DownloadAttachments, + ModRoles: formConfig.ModRoles, + AdminRoles: formConfig.AdminRoles, + TicketOpenMSG: formConfig.TicketOpenMSG, + } + + err := model.UpsertG(ctx, true, []string{"guild_id"}, boil.Infer(), boil.Infer()) + return templateData, err +} + +var _ web.PluginWithServerHomeWidget = (*Plugin)(nil) + +func (p *Plugin) LoadServerHomeWidget(w http.ResponseWriter, r *http.Request) (web.TemplateData, error) { + activeGuild, templateData := web.GetBaseCPContextData(r.Context()) + + settings, err := models.FindTicketConfigG(r.Context(), activeGuild.ID) + if err != nil && err != sql.ErrNoRows { + return templateData, err + } + + enabled := false + if settings != nil { + enabled = true + } + + templateData["WidgetTitle"] = "Tickets" + templateData["SettingsPath"] = "/tickets/settings" + if enabled { + templateData["WidgetEnabled"] = true + } else { + templateData["WidgetDisabled"] = true + } + + const format = `` + + templateData["WidgetBody"] = template.HTML(fmt.Sprintf(format, web.EnabledDisabledSpanStatus(enabled))) + + return templateData, nil +} diff --git a/verification/schema.go b/verification/schema.go index 2b1be3c144..c3b47dcc4f 100644 --- a/verification/schema.go +++ b/verification/schema.go @@ -1,31 +1,31 @@ -package verification - -const DBSchema = ` -CREATE TABLE IF NOT EXISTS verification_configs ( - guild_id BIGINT PRIMARY KEY, - - enabled BOOLEAN NOT NULL, - - verified_role BIGINT NOT NULL, - - page_content TEXT NOT NULL, - - kick_unverified_after INT NOT NULL, - warn_unverified_after INT NOT NULL, - warn_message TEXT NOT NULL, - - log_channel BIGINT NOT NULL -); - -ALTER TABLE verification_configs ADD COLUMN IF NOT EXISTS dm_message TEXT NOT NULL DEFAULT ''; - -CREATE TABLE IF NOT EXISTS verification_sessions ( - token TEXT PRIMARY KEY, - user_id BIGINT NOT NULL, - guild_id BIGINT NOT NULL, - created_at TIMESTAMP WITH TIME ZONE NOT NULL, - - solved_at TIMESTAMP WITH TIME ZONE, - expired_at TIMESTAMP WITH TIME ZONE -); -` +package verification + +const DBSchema = ` +CREATE TABLE IF NOT EXISTS verification_configs ( + guild_id BIGINT PRIMARY KEY, + + enabled BOOLEAN NOT NULL, + + verified_role BIGINT NOT NULL, + + page_content TEXT NOT NULL, + + kick_unverified_after INT NOT NULL, + warn_unverified_after INT NOT NULL, + warn_message TEXT NOT NULL, + + log_channel BIGINT NOT NULL +); + +ALTER TABLE verification_configs ADD COLUMN IF NOT EXISTS dm_message TEXT NOT NULL DEFAULT ''; + +CREATE TABLE IF NOT EXISTS verification_sessions ( + token TEXT PRIMARY KEY, + user_id BIGINT NOT NULL, + guild_id BIGINT NOT NULL, + created_at TIMESTAMP WITH TIME ZONE NOT NULL, + + solved_at TIMESTAMP WITH TIME ZONE, + expired_at TIMESTAMP WITH TIME ZONE +); +` diff --git a/verification/verification_web.go b/verification/verification_web.go index d0e24224d4..5586c5f017 100644 --- a/verification/verification_web.go +++ b/verification/verification_web.go @@ -1,300 +1,300 @@ -package verification - -import ( - "database/sql" - "encoding/json" - "fmt" - "github.com/jonas747/yagpdb/common" - "github.com/jonas747/yagpdb/common/scheduledevents2" - "github.com/jonas747/yagpdb/verification/models" - "github.com/jonas747/yagpdb/web" - "github.com/microcosm-cc/bluemonday" - "github.com/russross/blackfriday" - "github.com/volatiletech/null" - "github.com/volatiletech/sqlboiler/boil" - "goji.io/pat" - "html" - "html/template" - "net/http" - "net/url" - "strconv" - "time" -) - -type FormData struct { - Enabled bool - VerifiedRole int64 `valid:"role"` - PageContent string `valid:",10000"` - KickUnverifiedAfter int - WarnUnverifiedAfter int - WarnMessage string `valid:"template,10000"` - DMMessage string `valid:"template,10000"` - LogChannel int64 `valid:"channel,true"` -} - -func (p *Plugin) InitWeb() { - web.LoadHTMLTemplate("../../verification/assets/verification_control_panel.html", "templates/plugins/verification_control_panel.html") - web.LoadHTMLTemplate("../../verification/assets/verification_verify_page.html", "templates/plugins/verification_verify_page.html") - - web.AddSidebarItem(web.SidebarCategoryTools, &web.SidebarItem{ - Name: "Verification", - URL: "verification", - }) - - getHandler := web.ControllerHandler(p.handleGetSettings, "cp_verification_settings") - postHandler := web.ControllerPostHandler(p.handlePostSettings, getHandler, FormData{}, "Updated verification settings") - - web.CPMux.Handle(pat.Get("/verification"), web.RequireBotMemberMW(web.RequireGuildChannelsMiddleware(getHandler))) - web.CPMux.Handle(pat.Get("/verification/"), web.RequireBotMemberMW(web.RequireGuildChannelsMiddleware(getHandler))) - - web.CPMux.Handle(pat.Post("/verification"), web.RequireGuildChannelsMiddleware(postHandler)) - - getVerifyPageHandler := web.ControllerHandler(p.handleGetVerifyPage, "verification_verify_page") - postVerifyPageHandler := web.ControllerPostHandler(p.handlePostVerifyPage, getVerifyPageHandler, nil, "verification_verify_page") - web.ServerPublicMux.Handle(pat.Get("/verify/:user_id/:token"), getVerifyPageHandler) - web.ServerPublicMux.Handle(pat.Post("/verify/:user_id/:token"), postVerifyPageHandler) -} - -func (p *Plugin) handleGetSettings(w http.ResponseWriter, r *http.Request) (web.TemplateData, error) { - ctx := r.Context() - g, templateData := web.GetBaseCPContextData(ctx) - - settings, err := models.FindVerificationConfigG(ctx, g.ID) - if err == sql.ErrNoRows { - settings = &models.VerificationConfig{ - GuildID: g.ID, - } - err = nil - } - - if settings != nil && settings.DMMessage == "" { - settings.DMMessage = DefaultDMMessage - } - - templateData["DefaultPageContent"] = DefaultPageContent - templateData["PluginSettings"] = settings - - return templateData, err -} - -func (p *Plugin) handlePostSettings(w http.ResponseWriter, r *http.Request) (web.TemplateData, error) { - ctx := r.Context() - g, templateData := web.GetBaseCPContextData(ctx) - - formConfig := ctx.Value(common.ContextKeyParsedForm).(*FormData) - - model := &models.VerificationConfig{ - GuildID: g.ID, - Enabled: formConfig.Enabled, - VerifiedRole: formConfig.VerifiedRole, - PageContent: formConfig.PageContent, - KickUnverifiedAfter: formConfig.KickUnverifiedAfter, - WarnUnverifiedAfter: formConfig.WarnUnverifiedAfter, - WarnMessage: formConfig.WarnMessage, - LogChannel: formConfig.LogChannel, - DMMessage: formConfig.DMMessage, - } - - columns := boil.Whitelist("enabled", "verified_role", "page_content", "kick_unverified_after", "warn_unverified_after", "warn_message", "log_channel", "dm_message") - columnsCreate := boil.Whitelist("guild_id", "enabled", "verified_role", "page_content", "kick_unverified_after", "warn_unverified_after", "warn_message", "log_channel", "dm_message") - err := model.UpsertG(ctx, true, []string{"guild_id"}, columns, columnsCreate) - return templateData, err -} - -func (p *Plugin) handleGetVerifyPage(w http.ResponseWriter, r *http.Request) (web.TemplateData, error) { - ctx := r.Context() - g, templateData := web.GetBaseCPContextData(ctx) - - // render main page content - settings, err := models.FindVerificationConfigG(ctx, g.ID) - if err == sql.ErrNoRows { - settings = &models.VerificationConfig{ - GuildID: g.ID, - } - err = nil - } - - if err != nil { - return templateData, err - } - - if !settings.Enabled { - templateData.AddAlerts(web.ErrorAlert("Verification system disabled on this server")) - return templateData, nil - } - - if _, ok := templateData["REValid"]; !ok { - // check if there's a valid session if we didn't just finish verifying - userID, _ := strconv.ParseInt(pat.Param(r, "user_id"), 10, 64) - token := pat.Param(r, "token") - _, err = models.VerificationSessions( - models.VerificationSessionWhere.UserID.EQ(userID), - models.VerificationSessionWhere.Token.EQ(token), - models.VerificationSessionWhere.ExpiredAt.IsNull(), - models.VerificationSessionWhere.SolvedAt.IsNull()).OneG(ctx) - - if err != nil { - if err == sql.ErrNoRows { - templateData.AddAlerts(web.ErrorAlert("No verification session, try rejoining the server or contact an admin if the problem persist")) - return templateData, nil - } - - return templateData, err - } - } - - templateData["ExtraHead"] = template.HTML(``) - templateData["GoogleReCaptchaSiteKey"] = confGoogleReCAPTCHASiteKey.GetString() - - msg := settings.PageContent - if msg == "" { - msg = DefaultPageContent - } - - unsafe := blackfriday.MarkdownCommon([]byte(msg)) - html := bluemonday.UGCPolicy().SanitizeBytes(unsafe) - templateData["RenderedPageContent"] = template.HTML(html) - - return templateData, nil -} - -func (p *Plugin) handlePostVerifyPage(w http.ResponseWriter, r *http.Request) (web.TemplateData, error) { - ctx := r.Context() - g, templateData := web.GetBaseCPContextData(ctx) - - settings, err := models.FindVerificationConfigG(ctx, g.ID) - if err == sql.ErrNoRows { - settings = &models.VerificationConfig{ - GuildID: g.ID, - } - err = nil - } - - if err != nil { - return templateData, err - } - - if !settings.Enabled { - templateData.AddAlerts(web.ErrorAlert("Verification system disabled on this server")) - return templateData, nil - } - - valid, err := p.checkCAPTCHAResponse(r.FormValue("g-recaptcha-response")) - - token := pat.Param(r, "token") - userID, _ := strconv.ParseInt(pat.Param(r, "user_id"), 10, 64) - - verSession, err := models.VerificationSessions( - models.VerificationSessionWhere.UserID.EQ(userID), - models.VerificationSessionWhere.Token.EQ(token), - models.VerificationSessionWhere.ExpiredAt.IsNull(), - models.VerificationSessionWhere.SolvedAt.IsNull()).OneG(ctx) - - if err != nil { - if err == sql.ErrNoRows { - templateData.AddAlerts(web.ErrorAlert("No verification session, try rejoining the server or contact an admin if the problem persist")) - return templateData, nil - } - - return templateData, err - } - - if valid { - scheduledevents2.ScheduleEvent("verification_user_verified", g.ID, time.Now(), userID) - verSession.SolvedAt = null.TimeFrom(time.Now()) - verSession.UpdateG(ctx, boil.Infer()) - } else { - templateData.AddAlerts(web.ErrorAlert("Invalid reCAPTCHA submission.")) - } - - templateData["REValid"] = valid - - return templateData, err -} - -type CheckCAPTCHAResponse struct { - Success bool `json:"success"` - ChallengeTS string `json:"challenge_ts"` - Hostname string `json:"hostname"` - ErrorCodes []string `json:"error-codes"` -} - -type CheckCAPTCHARequest struct { - Secret string `json:"secret"` - Response string `json:"response"` -} - -func (p *Plugin) checkCAPTCHAResponse(response string) (valid bool, err error) { - - v := url.Values{ - "response": {response}, - "secret": {confGoogleReCAPTCHASecret.GetString()}, - } - - resp, err := http.PostForm("https://www.google.com/recaptcha/api/siteverify", v) - if err != nil { - return false, err - } - - defer resp.Body.Close() - - var dst CheckCAPTCHAResponse - decoder := json.NewDecoder(resp.Body) - err = decoder.Decode(&dst) - if err != nil { - return false, err - } - - if !dst.Success { - logger.Warnf("reCAPTCHA failed: %#v", dst) - } - - return dst.Success, nil -} - -var _ web.PluginWithServerHomeWidget = (*Plugin)(nil) - -func (p *Plugin) LoadServerHomeWidget(w http.ResponseWriter, r *http.Request) (web.TemplateData, error) { - ag, templateData := web.GetBaseCPContextData(r.Context()) - ctx := r.Context() - - templateData["WidgetTitle"] = "Google reCAPTCHA Verification" - templateData["SettingsPath"] = "/verification" - - settings, err := models.FindVerificationConfigG(ctx, ag.ID) - if err != nil { - if err == sql.ErrNoRows { - settings = &models.VerificationConfig{ - GuildID: ag.ID, - } - } else { - return templateData, err - } - } - - format := `` - - status := web.EnabledDisabledSpanStatus(settings.Enabled) - - if settings.Enabled { - templateData["WidgetEnabled"] = true - } else { - templateData["WidgetDisabled"] = true - } - - roleStr := "none / unknown" - indicatorRole := "" - if role := ag.Role(settings.VerifiedRole); role != nil { - roleStr = html.EscapeString(role.Name) - indicatorRole = web.Indicator(true) - } else { - indicatorRole = web.Indicator(false) - } - - templateData["WidgetBody"] = template.HTML(fmt.Sprintf(format, status, roleStr, indicatorRole)) - - return templateData, nil -} +package verification + +import ( + "database/sql" + "encoding/json" + "fmt" + "github.com/jonas747/yagpdb/common" + "github.com/jonas747/yagpdb/common/scheduledevents2" + "github.com/jonas747/yagpdb/verification/models" + "github.com/jonas747/yagpdb/web" + "github.com/microcosm-cc/bluemonday" + "github.com/russross/blackfriday" + "github.com/volatiletech/null" + "github.com/volatiletech/sqlboiler/boil" + "goji.io/pat" + "html" + "html/template" + "net/http" + "net/url" + "strconv" + "time" +) + +type FormData struct { + Enabled bool + VerifiedRole int64 `valid:"role"` + PageContent string `valid:",10000"` + KickUnverifiedAfter int + WarnUnverifiedAfter int + WarnMessage string `valid:"template,10000"` + DMMessage string `valid:"template,10000"` + LogChannel int64 `valid:"channel,true"` +} + +func (p *Plugin) InitWeb() { + web.LoadHTMLTemplate("../../verification/assets/verification_control_panel.html", "templates/plugins/verification_control_panel.html") + web.LoadHTMLTemplate("../../verification/assets/verification_verify_page.html", "templates/plugins/verification_verify_page.html") + + web.AddSidebarItem(web.SidebarCategoryTools, &web.SidebarItem{ + Name: "Verification", + URL: "verification", + }) + + getHandler := web.ControllerHandler(p.handleGetSettings, "cp_verification_settings") + postHandler := web.ControllerPostHandler(p.handlePostSettings, getHandler, FormData{}, "Updated verification settings") + + web.CPMux.Handle(pat.Get("/verification"), web.RequireBotMemberMW(web.RequireGuildChannelsMiddleware(getHandler))) + web.CPMux.Handle(pat.Get("/verification/"), web.RequireBotMemberMW(web.RequireGuildChannelsMiddleware(getHandler))) + + web.CPMux.Handle(pat.Post("/verification"), web.RequireGuildChannelsMiddleware(postHandler)) + + getVerifyPageHandler := web.ControllerHandler(p.handleGetVerifyPage, "verification_verify_page") + postVerifyPageHandler := web.ControllerPostHandler(p.handlePostVerifyPage, getVerifyPageHandler, nil, "verification_verify_page") + web.ServerPublicMux.Handle(pat.Get("/verify/:user_id/:token"), getVerifyPageHandler) + web.ServerPublicMux.Handle(pat.Post("/verify/:user_id/:token"), postVerifyPageHandler) +} + +func (p *Plugin) handleGetSettings(w http.ResponseWriter, r *http.Request) (web.TemplateData, error) { + ctx := r.Context() + g, templateData := web.GetBaseCPContextData(ctx) + + settings, err := models.FindVerificationConfigG(ctx, g.ID) + if err == sql.ErrNoRows { + settings = &models.VerificationConfig{ + GuildID: g.ID, + } + err = nil + } + + if settings != nil && settings.DMMessage == "" { + settings.DMMessage = DefaultDMMessage + } + + templateData["DefaultPageContent"] = DefaultPageContent + templateData["PluginSettings"] = settings + + return templateData, err +} + +func (p *Plugin) handlePostSettings(w http.ResponseWriter, r *http.Request) (web.TemplateData, error) { + ctx := r.Context() + g, templateData := web.GetBaseCPContextData(ctx) + + formConfig := ctx.Value(common.ContextKeyParsedForm).(*FormData) + + model := &models.VerificationConfig{ + GuildID: g.ID, + Enabled: formConfig.Enabled, + VerifiedRole: formConfig.VerifiedRole, + PageContent: formConfig.PageContent, + KickUnverifiedAfter: formConfig.KickUnverifiedAfter, + WarnUnverifiedAfter: formConfig.WarnUnverifiedAfter, + WarnMessage: formConfig.WarnMessage, + LogChannel: formConfig.LogChannel, + DMMessage: formConfig.DMMessage, + } + + columns := boil.Whitelist("enabled", "verified_role", "page_content", "kick_unverified_after", "warn_unverified_after", "warn_message", "log_channel", "dm_message") + columnsCreate := boil.Whitelist("guild_id", "enabled", "verified_role", "page_content", "kick_unverified_after", "warn_unverified_after", "warn_message", "log_channel", "dm_message") + err := model.UpsertG(ctx, true, []string{"guild_id"}, columns, columnsCreate) + return templateData, err +} + +func (p *Plugin) handleGetVerifyPage(w http.ResponseWriter, r *http.Request) (web.TemplateData, error) { + ctx := r.Context() + g, templateData := web.GetBaseCPContextData(ctx) + + // render main page content + settings, err := models.FindVerificationConfigG(ctx, g.ID) + if err == sql.ErrNoRows { + settings = &models.VerificationConfig{ + GuildID: g.ID, + } + err = nil + } + + if err != nil { + return templateData, err + } + + if !settings.Enabled { + templateData.AddAlerts(web.ErrorAlert("Verification system disabled on this server")) + return templateData, nil + } + + if _, ok := templateData["REValid"]; !ok { + // check if there's a valid session if we didn't just finish verifying + userID, _ := strconv.ParseInt(pat.Param(r, "user_id"), 10, 64) + token := pat.Param(r, "token") + _, err = models.VerificationSessions( + models.VerificationSessionWhere.UserID.EQ(userID), + models.VerificationSessionWhere.Token.EQ(token), + models.VerificationSessionWhere.ExpiredAt.IsNull(), + models.VerificationSessionWhere.SolvedAt.IsNull()).OneG(ctx) + + if err != nil { + if err == sql.ErrNoRows { + templateData.AddAlerts(web.ErrorAlert("No verification session, try rejoining the server or contact an admin if the problem persist")) + return templateData, nil + } + + return templateData, err + } + } + + templateData["ExtraHead"] = template.HTML(``) + templateData["GoogleReCaptchaSiteKey"] = confGoogleReCAPTCHASiteKey.GetString() + + msg := settings.PageContent + if msg == "" { + msg = DefaultPageContent + } + + unsafe := blackfriday.MarkdownCommon([]byte(msg)) + html := bluemonday.UGCPolicy().SanitizeBytes(unsafe) + templateData["RenderedPageContent"] = template.HTML(html) + + return templateData, nil +} + +func (p *Plugin) handlePostVerifyPage(w http.ResponseWriter, r *http.Request) (web.TemplateData, error) { + ctx := r.Context() + g, templateData := web.GetBaseCPContextData(ctx) + + settings, err := models.FindVerificationConfigG(ctx, g.ID) + if err == sql.ErrNoRows { + settings = &models.VerificationConfig{ + GuildID: g.ID, + } + err = nil + } + + if err != nil { + return templateData, err + } + + if !settings.Enabled { + templateData.AddAlerts(web.ErrorAlert("Verification system disabled on this server")) + return templateData, nil + } + + valid, err := p.checkCAPTCHAResponse(r.FormValue("g-recaptcha-response")) + + token := pat.Param(r, "token") + userID, _ := strconv.ParseInt(pat.Param(r, "user_id"), 10, 64) + + verSession, err := models.VerificationSessions( + models.VerificationSessionWhere.UserID.EQ(userID), + models.VerificationSessionWhere.Token.EQ(token), + models.VerificationSessionWhere.ExpiredAt.IsNull(), + models.VerificationSessionWhere.SolvedAt.IsNull()).OneG(ctx) + + if err != nil { + if err == sql.ErrNoRows { + templateData.AddAlerts(web.ErrorAlert("No verification session, try rejoining the server or contact an admin if the problem persist")) + return templateData, nil + } + + return templateData, err + } + + if valid { + scheduledevents2.ScheduleEvent("verification_user_verified", g.ID, time.Now(), userID) + verSession.SolvedAt = null.TimeFrom(time.Now()) + verSession.UpdateG(ctx, boil.Infer()) + } else { + templateData.AddAlerts(web.ErrorAlert("Invalid reCAPTCHA submission.")) + } + + templateData["REValid"] = valid + + return templateData, err +} + +type CheckCAPTCHAResponse struct { + Success bool `json:"success"` + ChallengeTS string `json:"challenge_ts"` + Hostname string `json:"hostname"` + ErrorCodes []string `json:"error-codes"` +} + +type CheckCAPTCHARequest struct { + Secret string `json:"secret"` + Response string `json:"response"` +} + +func (p *Plugin) checkCAPTCHAResponse(response string) (valid bool, err error) { + + v := url.Values{ + "response": {response}, + "secret": {confGoogleReCAPTCHASecret.GetString()}, + } + + resp, err := http.PostForm("https://www.google.com/recaptcha/api/siteverify", v) + if err != nil { + return false, err + } + + defer resp.Body.Close() + + var dst CheckCAPTCHAResponse + decoder := json.NewDecoder(resp.Body) + err = decoder.Decode(&dst) + if err != nil { + return false, err + } + + if !dst.Success { + logger.Warnf("reCAPTCHA failed: %#v", dst) + } + + return dst.Success, nil +} + +var _ web.PluginWithServerHomeWidget = (*Plugin)(nil) + +func (p *Plugin) LoadServerHomeWidget(w http.ResponseWriter, r *http.Request) (web.TemplateData, error) { + ag, templateData := web.GetBaseCPContextData(r.Context()) + ctx := r.Context() + + templateData["WidgetTitle"] = "Google reCAPTCHA Verification" + templateData["SettingsPath"] = "/verification" + + settings, err := models.FindVerificationConfigG(ctx, ag.ID) + if err != nil { + if err == sql.ErrNoRows { + settings = &models.VerificationConfig{ + GuildID: ag.ID, + } + } else { + return templateData, err + } + } + + format := `` + + status := web.EnabledDisabledSpanStatus(settings.Enabled) + + if settings.Enabled { + templateData["WidgetEnabled"] = true + } else { + templateData["WidgetDisabled"] = true + } + + roleStr := "none / unknown" + indicatorRole := "" + if role := ag.Role(settings.VerifiedRole); role != nil { + roleStr = html.EscapeString(role.Name) + indicatorRole = web.Indicator(true) + } else { + indicatorRole = web.Indicator(false) + } + + templateData["WidgetBody"] = template.HTML(fmt.Sprintf(format, status, roleStr, indicatorRole)) + + return templateData, nil +} diff --git a/web/web_test.go b/web/web_test.go index bdedfd0f4a..fff4a760f1 100644 --- a/web/web_test.go +++ b/web/web_test.go @@ -1,632 +1,632 @@ -package web - -import ( - "fmt" - "github.com/jonas747/discordgo" - "github.com/jonas747/yagpdb/common" - "github.com/jonas747/yagpdb/common/models" - "testing" -) - -func createUserGuild(connected bool, owner bool, manageServer bool) *common.GuildWithConnected { - perms := 0 - if manageServer { - perms = discordgo.PermissionManageServer - } - - if owner { - perms |= discordgo.PermissionManageServer | discordgo.PermissionAll - } - - return &common.GuildWithConnected{ - Connected: connected, - UserGuild: &discordgo.UserGuild{ - Owner: owner, - Permissions: perms, - }, - } -} - -func TestHasAccesstoGuildSettings(t *testing.T) { - - type TestCase struct { - Name string - Conf *models.CoreConfig - GWC *common.GuildWithConnected - Roles []int64 - IsMember bool - ReadOnly bool - - ShouldHaveAcces bool - } - - testCases := []*TestCase{ - - ////////////////////////// - // default settings tests - ///////////////////////// - - // default settings, non member access - &TestCase{ - Name: "default settings non member access (ro)", - Conf: &models.CoreConfig{}, - GWC: createUserGuild(true, false, false), - Roles: nil, - IsMember: false, - ReadOnly: true, - - ShouldHaveAcces: false, - }, - &TestCase{ - Name: "default settings non member access", - Conf: &models.CoreConfig{}, - GWC: createUserGuild(true, false, false), - Roles: nil, - IsMember: false, - ReadOnly: false, - - ShouldHaveAcces: false, - }, - - // default settings normal member access - &TestCase{ - Name: "default settings normal normal member access (ro)", - Conf: &models.CoreConfig{}, - GWC: createUserGuild(true, false, false), - Roles: nil, - IsMember: true, - ReadOnly: true, - - ShouldHaveAcces: false, - }, - &TestCase{ - Name: "default settings normal user access", - Conf: &models.CoreConfig{}, - GWC: createUserGuild(true, false, false), - Roles: nil, - IsMember: true, - ReadOnly: false, - - ShouldHaveAcces: false, - }, - - // default settings admin user access - &TestCase{ - Name: "default settings admin user access (ro)", - Conf: &models.CoreConfig{}, - GWC: createUserGuild(true, false, true), - Roles: nil, - IsMember: true, - ReadOnly: true, - - ShouldHaveAcces: true, - }, - &TestCase{ - Name: "default settings admin user access", - Conf: &models.CoreConfig{}, - GWC: createUserGuild(true, false, true), - Roles: nil, - IsMember: true, - ReadOnly: false, - - ShouldHaveAcces: true, - }, - - // default settings owner user access - &TestCase{ - Name: "default settings owner user access (ro)", - Conf: &models.CoreConfig{}, - GWC: createUserGuild(true, true, false), - Roles: nil, - IsMember: true, - ReadOnly: true, - - ShouldHaveAcces: true, - }, - &TestCase{ - Name: "default settings owner user access", - Conf: &models.CoreConfig{}, - GWC: createUserGuild(true, true, false), - Roles: nil, - IsMember: true, - ReadOnly: false, - - ShouldHaveAcces: true, - }, - - //////////////////////////////////// - // AllowNonMembersROAccess tests - //////////////////////////////////// - - // all users ro - normal user access - &TestCase{ - Name: "all users ro-normal user access (ro)", - Conf: &models.CoreConfig{ - AllowNonMembersReadOnly: true, - }, - GWC: createUserGuild(true, false, false), - Roles: nil, - IsMember: false, - ReadOnly: true, - - ShouldHaveAcces: true, - }, - &TestCase{ - Name: "all users ro-normal user access", - Conf: &models.CoreConfig{ - AllowNonMembersReadOnly: true, - }, - GWC: createUserGuild(true, false, false), - Roles: nil, - IsMember: false, - ReadOnly: false, - - ShouldHaveAcces: false, - }, - // all users ro - member access - &TestCase{ - Name: "all users ro-member access (ro)", - Conf: &models.CoreConfig{ - AllowNonMembersReadOnly: true, - }, - GWC: createUserGuild(true, false, false), - Roles: nil, - IsMember: true, - ReadOnly: true, - - ShouldHaveAcces: true, - }, - &TestCase{ - Name: "all users ro-member access", - Conf: &models.CoreConfig{ - AllowNonMembersReadOnly: true, - }, - GWC: createUserGuild(true, false, false), - Roles: nil, - IsMember: true, - ReadOnly: false, - - ShouldHaveAcces: false, - }, - // all users ro - admin access - &TestCase{ - Name: "all users ro-admin access (ro)", - Conf: &models.CoreConfig{ - AllowNonMembersReadOnly: true, - }, - GWC: createUserGuild(true, false, true), - Roles: nil, - IsMember: true, - ReadOnly: true, - - ShouldHaveAcces: true, - }, - &TestCase{ - Name: "all users ro-admin access", - Conf: &models.CoreConfig{ - AllowNonMembersReadOnly: true, - }, - GWC: createUserGuild(true, false, true), - Roles: nil, - IsMember: true, - ReadOnly: false, - - ShouldHaveAcces: true, - }, - - //////////////////////////////////// - // AllMembersRO tests - //////////////////////////////////// - - // all members ro - normal user access - &TestCase{ - Name: "all members ro-normal user access (ro)", - Conf: &models.CoreConfig{ - AllowAllMembersReadOnly: true, - }, - GWC: createUserGuild(true, false, false), - Roles: nil, - IsMember: false, - ReadOnly: true, - - ShouldHaveAcces: false, - }, - &TestCase{ - Name: "all members ro-normal user access", - Conf: &models.CoreConfig{ - AllowAllMembersReadOnly: true, - }, - GWC: createUserGuild(true, false, false), - Roles: nil, - IsMember: false, - ReadOnly: false, - - ShouldHaveAcces: false, - }, - // all members ro - member access - &TestCase{ - Name: "all members ro-member access (ro)", - Conf: &models.CoreConfig{ - AllowAllMembersReadOnly: true, - }, - GWC: createUserGuild(true, false, false), - Roles: nil, - IsMember: true, - ReadOnly: true, - - ShouldHaveAcces: true, - }, - &TestCase{ - Name: "all members ro-member access", - Conf: &models.CoreConfig{ - AllowAllMembersReadOnly: true, - }, - GWC: createUserGuild(true, false, false), - Roles: nil, - IsMember: true, - ReadOnly: false, - - ShouldHaveAcces: false, - }, - // all members ro - admin access - &TestCase{ - Name: "all members ro-admin access (ro)", - Conf: &models.CoreConfig{ - AllowAllMembersReadOnly: true, - }, - GWC: createUserGuild(true, false, true), - Roles: nil, - IsMember: true, - ReadOnly: true, - - ShouldHaveAcces: true, - }, - &TestCase{ - Name: "all members ro-admin access", - Conf: &models.CoreConfig{ - AllowAllMembersReadOnly: true, - }, - GWC: createUserGuild(true, false, true), - Roles: nil, - IsMember: true, - ReadOnly: false, - - ShouldHaveAcces: true, - }, - - //////////////////////////////////// - // Read only roles - //////////////////////////////////// - - // ro roles - normal user access - &TestCase{ - Name: "ro roles-normal user access (ro)", - Conf: &models.CoreConfig{ - AllowedReadOnlyRoles: []int64{5, 6}, - }, - GWC: createUserGuild(true, false, false), - Roles: nil, - IsMember: false, - ReadOnly: true, - - ShouldHaveAcces: false, - }, - &TestCase{ - Name: "ro roles-normal user access", - Conf: &models.CoreConfig{ - AllowedReadOnlyRoles: []int64{5, 6}, - }, - GWC: createUserGuild(true, false, false), - Roles: nil, - IsMember: false, - ReadOnly: false, - - ShouldHaveAcces: false, - }, - - // ro roles - member no roles - &TestCase{ - Name: "ro roles-member no roles (ro)", - Conf: &models.CoreConfig{ - AllowedReadOnlyRoles: []int64{5, 6}, - }, - GWC: createUserGuild(true, false, false), - Roles: nil, - IsMember: true, - ReadOnly: true, - - ShouldHaveAcces: false, - }, - &TestCase{ - Name: "ro roles-member no roles", - Conf: &models.CoreConfig{ - AllowedReadOnlyRoles: []int64{5, 6}, - }, - GWC: createUserGuild(true, false, false), - Roles: nil, - IsMember: true, - ReadOnly: false, - - ShouldHaveAcces: false, - }, - - // ro roles - member access one role - &TestCase{ - Name: "ro roles-member access one role (ro)", - Conf: &models.CoreConfig{ - AllowedReadOnlyRoles: []int64{5, 6}, - }, - GWC: createUserGuild(true, false, false), - Roles: []int64{5}, - IsMember: true, - ReadOnly: true, - - ShouldHaveAcces: true, - }, - &TestCase{ - Name: "ro roles-member access one role", - Conf: &models.CoreConfig{ - AllowedReadOnlyRoles: []int64{5, 6}, - }, - GWC: createUserGuild(true, false, false), - Roles: []int64{5}, - IsMember: true, - ReadOnly: false, - - ShouldHaveAcces: false, - }, - - // ro roles - member access other role - &TestCase{ - Name: "ro roles-member access other role (ro)", - Conf: &models.CoreConfig{ - AllowedReadOnlyRoles: []int64{5, 6}, - }, - GWC: createUserGuild(true, false, false), - Roles: []int64{6}, - IsMember: true, - ReadOnly: true, - - ShouldHaveAcces: true, - }, - &TestCase{ - Name: "ro roles-member access other role", - Conf: &models.CoreConfig{ - AllowedReadOnlyRoles: []int64{5, 6}, - }, - GWC: createUserGuild(true, false, false), - Roles: []int64{6}, - IsMember: true, - ReadOnly: false, - - ShouldHaveAcces: false, - }, - - // ro roles - member access both roles - &TestCase{ - Name: "ro roles - member access both roles (ro)", - Conf: &models.CoreConfig{ - AllowedReadOnlyRoles: []int64{5, 6}, - }, - GWC: createUserGuild(true, false, false), - Roles: []int64{5, 6}, - IsMember: true, - ReadOnly: true, - - ShouldHaveAcces: true, - }, - &TestCase{ - Name: "ro roles - member access both roles", - Conf: &models.CoreConfig{ - AllowedReadOnlyRoles: []int64{5, 6}, - }, - GWC: createUserGuild(true, false, false), - Roles: []int64{5, 6}, - IsMember: true, - ReadOnly: false, - - ShouldHaveAcces: false, - }, - - // ro roles - admin access - &TestCase{ - Name: "ro roles-admin access (ro)", - Conf: &models.CoreConfig{ - AllowedReadOnlyRoles: []int64{5, 6}, - }, - GWC: createUserGuild(true, false, true), - Roles: nil, - IsMember: true, - ReadOnly: true, - - ShouldHaveAcces: true, - }, - &TestCase{ - Name: "ro roles-admin access", - Conf: &models.CoreConfig{ - AllowedReadOnlyRoles: []int64{5, 6}, - }, - GWC: createUserGuild(true, false, true), - Roles: nil, - IsMember: true, - ReadOnly: false, - - ShouldHaveAcces: true, - }, - - //////////////////////////////////// - // Write roles - //////////////////////////////////// - - // write roles - normal user access - &TestCase{ - Name: "write roles-normal user access (ro)", - Conf: &models.CoreConfig{ - AllowedWriteRoles: []int64{5, 6}, - }, - GWC: createUserGuild(true, false, false), - Roles: nil, - IsMember: false, - ReadOnly: true, - - ShouldHaveAcces: false, - }, - &TestCase{ - Name: "write roles-normal user access", - Conf: &models.CoreConfig{ - AllowedWriteRoles: []int64{5, 6}, - }, - GWC: createUserGuild(true, false, false), - Roles: nil, - IsMember: false, - ReadOnly: false, - - ShouldHaveAcces: false, - }, - - // write roles - member no roles - &TestCase{ - Name: "write roles-member no roles (ro)", - Conf: &models.CoreConfig{ - AllowedWriteRoles: []int64{5, 6}, - }, - GWC: createUserGuild(true, false, false), - Roles: nil, - IsMember: true, - ReadOnly: true, - - ShouldHaveAcces: false, - }, - &TestCase{ - Name: "write roles-member no roles", - Conf: &models.CoreConfig{ - AllowedWriteRoles: []int64{5, 6}, - }, - GWC: createUserGuild(true, false, false), - Roles: nil, - IsMember: true, - ReadOnly: false, - - ShouldHaveAcces: false, - }, - - // write roles - member access one role - &TestCase{ - Name: "write roles-member access one role (ro)", - Conf: &models.CoreConfig{ - AllowedWriteRoles: []int64{5, 6}, - }, - GWC: createUserGuild(true, false, false), - Roles: []int64{5}, - IsMember: true, - ReadOnly: true, - - ShouldHaveAcces: true, - }, - &TestCase{ - Name: "write roles-member access one role", - Conf: &models.CoreConfig{ - AllowedWriteRoles: []int64{5, 6}, - }, - GWC: createUserGuild(true, false, false), - Roles: []int64{5}, - IsMember: true, - ReadOnly: false, - - ShouldHaveAcces: true, - }, - - // write roles - member access other role - &TestCase{ - Name: "write roles-member access other role (ro)", - Conf: &models.CoreConfig{ - AllowedWriteRoles: []int64{5, 6}, - }, - GWC: createUserGuild(true, false, false), - Roles: []int64{6}, - IsMember: true, - ReadOnly: true, - - ShouldHaveAcces: true, - }, - &TestCase{ - Name: "write roles-member access other role", - Conf: &models.CoreConfig{ - AllowedWriteRoles: []int64{5, 6}, - }, - GWC: createUserGuild(true, false, false), - Roles: []int64{6}, - IsMember: true, - ReadOnly: false, - - ShouldHaveAcces: true, - }, - - // write roles - member access both roles - &TestCase{ - Name: "write roles - member access both roles (ro)", - Conf: &models.CoreConfig{ - AllowedWriteRoles: []int64{5, 6}, - }, - GWC: createUserGuild(true, false, false), - Roles: []int64{5, 6}, - IsMember: true, - ReadOnly: true, - - ShouldHaveAcces: true, - }, - &TestCase{ - Name: "write roles - member access both roles", - Conf: &models.CoreConfig{ - AllowedWriteRoles: []int64{5, 6}, - }, - GWC: createUserGuild(true, false, false), - Roles: []int64{5, 6}, - IsMember: true, - ReadOnly: false, - - ShouldHaveAcces: true, - }, - - // write roles - admin access - &TestCase{ - Name: "write roles-admin access (ro)", - Conf: &models.CoreConfig{ - AllowedWriteRoles: []int64{5, 6}, - }, - GWC: createUserGuild(true, false, true), - Roles: nil, - IsMember: true, - ReadOnly: true, - - ShouldHaveAcces: true, - }, - &TestCase{ - Name: "write roles-admin access", - Conf: &models.CoreConfig{ - AllowedWriteRoles: []int64{5, 6}, - }, - GWC: createUserGuild(true, false, true), - Roles: nil, - IsMember: true, - ReadOnly: false, - - ShouldHaveAcces: true, - }, - } - - for i, v := range testCases { - t.Run(fmt.Sprintf("Case #%d_%s", i, v.Name), func(it *testing.T) { - userID := int64(0) - if v.IsMember { - userID = 10 - } - - result := HasAccesstoGuildSettings(userID, v.GWC, v.Conf, StaticRoleProvider(v.Roles), !v.ReadOnly) - if result != v.ShouldHaveAcces { - it.Errorf("incorrect result, got %t, wanted: %t", result, v.ShouldHaveAcces) - } - }) - } - -} +package web + +import ( + "fmt" + "github.com/jonas747/discordgo" + "github.com/jonas747/yagpdb/common" + "github.com/jonas747/yagpdb/common/models" + "testing" +) + +func createUserGuild(connected bool, owner bool, manageServer bool) *common.GuildWithConnected { + perms := 0 + if manageServer { + perms = discordgo.PermissionManageServer + } + + if owner { + perms |= discordgo.PermissionManageServer | discordgo.PermissionAll + } + + return &common.GuildWithConnected{ + Connected: connected, + UserGuild: &discordgo.UserGuild{ + Owner: owner, + Permissions: perms, + }, + } +} + +func TestHasAccesstoGuildSettings(t *testing.T) { + + type TestCase struct { + Name string + Conf *models.CoreConfig + GWC *common.GuildWithConnected + Roles []int64 + IsMember bool + ReadOnly bool + + ShouldHaveAcces bool + } + + testCases := []*TestCase{ + + ////////////////////////// + // default settings tests + ///////////////////////// + + // default settings, non member access + &TestCase{ + Name: "default settings non member access (ro)", + Conf: &models.CoreConfig{}, + GWC: createUserGuild(true, false, false), + Roles: nil, + IsMember: false, + ReadOnly: true, + + ShouldHaveAcces: false, + }, + &TestCase{ + Name: "default settings non member access", + Conf: &models.CoreConfig{}, + GWC: createUserGuild(true, false, false), + Roles: nil, + IsMember: false, + ReadOnly: false, + + ShouldHaveAcces: false, + }, + + // default settings normal member access + &TestCase{ + Name: "default settings normal normal member access (ro)", + Conf: &models.CoreConfig{}, + GWC: createUserGuild(true, false, false), + Roles: nil, + IsMember: true, + ReadOnly: true, + + ShouldHaveAcces: false, + }, + &TestCase{ + Name: "default settings normal user access", + Conf: &models.CoreConfig{}, + GWC: createUserGuild(true, false, false), + Roles: nil, + IsMember: true, + ReadOnly: false, + + ShouldHaveAcces: false, + }, + + // default settings admin user access + &TestCase{ + Name: "default settings admin user access (ro)", + Conf: &models.CoreConfig{}, + GWC: createUserGuild(true, false, true), + Roles: nil, + IsMember: true, + ReadOnly: true, + + ShouldHaveAcces: true, + }, + &TestCase{ + Name: "default settings admin user access", + Conf: &models.CoreConfig{}, + GWC: createUserGuild(true, false, true), + Roles: nil, + IsMember: true, + ReadOnly: false, + + ShouldHaveAcces: true, + }, + + // default settings owner user access + &TestCase{ + Name: "default settings owner user access (ro)", + Conf: &models.CoreConfig{}, + GWC: createUserGuild(true, true, false), + Roles: nil, + IsMember: true, + ReadOnly: true, + + ShouldHaveAcces: true, + }, + &TestCase{ + Name: "default settings owner user access", + Conf: &models.CoreConfig{}, + GWC: createUserGuild(true, true, false), + Roles: nil, + IsMember: true, + ReadOnly: false, + + ShouldHaveAcces: true, + }, + + //////////////////////////////////// + // AllowNonMembersROAccess tests + //////////////////////////////////// + + // all users ro - normal user access + &TestCase{ + Name: "all users ro-normal user access (ro)", + Conf: &models.CoreConfig{ + AllowNonMembersReadOnly: true, + }, + GWC: createUserGuild(true, false, false), + Roles: nil, + IsMember: false, + ReadOnly: true, + + ShouldHaveAcces: true, + }, + &TestCase{ + Name: "all users ro-normal user access", + Conf: &models.CoreConfig{ + AllowNonMembersReadOnly: true, + }, + GWC: createUserGuild(true, false, false), + Roles: nil, + IsMember: false, + ReadOnly: false, + + ShouldHaveAcces: false, + }, + // all users ro - member access + &TestCase{ + Name: "all users ro-member access (ro)", + Conf: &models.CoreConfig{ + AllowNonMembersReadOnly: true, + }, + GWC: createUserGuild(true, false, false), + Roles: nil, + IsMember: true, + ReadOnly: true, + + ShouldHaveAcces: true, + }, + &TestCase{ + Name: "all users ro-member access", + Conf: &models.CoreConfig{ + AllowNonMembersReadOnly: true, + }, + GWC: createUserGuild(true, false, false), + Roles: nil, + IsMember: true, + ReadOnly: false, + + ShouldHaveAcces: false, + }, + // all users ro - admin access + &TestCase{ + Name: "all users ro-admin access (ro)", + Conf: &models.CoreConfig{ + AllowNonMembersReadOnly: true, + }, + GWC: createUserGuild(true, false, true), + Roles: nil, + IsMember: true, + ReadOnly: true, + + ShouldHaveAcces: true, + }, + &TestCase{ + Name: "all users ro-admin access", + Conf: &models.CoreConfig{ + AllowNonMembersReadOnly: true, + }, + GWC: createUserGuild(true, false, true), + Roles: nil, + IsMember: true, + ReadOnly: false, + + ShouldHaveAcces: true, + }, + + //////////////////////////////////// + // AllMembersRO tests + //////////////////////////////////// + + // all members ro - normal user access + &TestCase{ + Name: "all members ro-normal user access (ro)", + Conf: &models.CoreConfig{ + AllowAllMembersReadOnly: true, + }, + GWC: createUserGuild(true, false, false), + Roles: nil, + IsMember: false, + ReadOnly: true, + + ShouldHaveAcces: false, + }, + &TestCase{ + Name: "all members ro-normal user access", + Conf: &models.CoreConfig{ + AllowAllMembersReadOnly: true, + }, + GWC: createUserGuild(true, false, false), + Roles: nil, + IsMember: false, + ReadOnly: false, + + ShouldHaveAcces: false, + }, + // all members ro - member access + &TestCase{ + Name: "all members ro-member access (ro)", + Conf: &models.CoreConfig{ + AllowAllMembersReadOnly: true, + }, + GWC: createUserGuild(true, false, false), + Roles: nil, + IsMember: true, + ReadOnly: true, + + ShouldHaveAcces: true, + }, + &TestCase{ + Name: "all members ro-member access", + Conf: &models.CoreConfig{ + AllowAllMembersReadOnly: true, + }, + GWC: createUserGuild(true, false, false), + Roles: nil, + IsMember: true, + ReadOnly: false, + + ShouldHaveAcces: false, + }, + // all members ro - admin access + &TestCase{ + Name: "all members ro-admin access (ro)", + Conf: &models.CoreConfig{ + AllowAllMembersReadOnly: true, + }, + GWC: createUserGuild(true, false, true), + Roles: nil, + IsMember: true, + ReadOnly: true, + + ShouldHaveAcces: true, + }, + &TestCase{ + Name: "all members ro-admin access", + Conf: &models.CoreConfig{ + AllowAllMembersReadOnly: true, + }, + GWC: createUserGuild(true, false, true), + Roles: nil, + IsMember: true, + ReadOnly: false, + + ShouldHaveAcces: true, + }, + + //////////////////////////////////// + // Read only roles + //////////////////////////////////// + + // ro roles - normal user access + &TestCase{ + Name: "ro roles-normal user access (ro)", + Conf: &models.CoreConfig{ + AllowedReadOnlyRoles: []int64{5, 6}, + }, + GWC: createUserGuild(true, false, false), + Roles: nil, + IsMember: false, + ReadOnly: true, + + ShouldHaveAcces: false, + }, + &TestCase{ + Name: "ro roles-normal user access", + Conf: &models.CoreConfig{ + AllowedReadOnlyRoles: []int64{5, 6}, + }, + GWC: createUserGuild(true, false, false), + Roles: nil, + IsMember: false, + ReadOnly: false, + + ShouldHaveAcces: false, + }, + + // ro roles - member no roles + &TestCase{ + Name: "ro roles-member no roles (ro)", + Conf: &models.CoreConfig{ + AllowedReadOnlyRoles: []int64{5, 6}, + }, + GWC: createUserGuild(true, false, false), + Roles: nil, + IsMember: true, + ReadOnly: true, + + ShouldHaveAcces: false, + }, + &TestCase{ + Name: "ro roles-member no roles", + Conf: &models.CoreConfig{ + AllowedReadOnlyRoles: []int64{5, 6}, + }, + GWC: createUserGuild(true, false, false), + Roles: nil, + IsMember: true, + ReadOnly: false, + + ShouldHaveAcces: false, + }, + + // ro roles - member access one role + &TestCase{ + Name: "ro roles-member access one role (ro)", + Conf: &models.CoreConfig{ + AllowedReadOnlyRoles: []int64{5, 6}, + }, + GWC: createUserGuild(true, false, false), + Roles: []int64{5}, + IsMember: true, + ReadOnly: true, + + ShouldHaveAcces: true, + }, + &TestCase{ + Name: "ro roles-member access one role", + Conf: &models.CoreConfig{ + AllowedReadOnlyRoles: []int64{5, 6}, + }, + GWC: createUserGuild(true, false, false), + Roles: []int64{5}, + IsMember: true, + ReadOnly: false, + + ShouldHaveAcces: false, + }, + + // ro roles - member access other role + &TestCase{ + Name: "ro roles-member access other role (ro)", + Conf: &models.CoreConfig{ + AllowedReadOnlyRoles: []int64{5, 6}, + }, + GWC: createUserGuild(true, false, false), + Roles: []int64{6}, + IsMember: true, + ReadOnly: true, + + ShouldHaveAcces: true, + }, + &TestCase{ + Name: "ro roles-member access other role", + Conf: &models.CoreConfig{ + AllowedReadOnlyRoles: []int64{5, 6}, + }, + GWC: createUserGuild(true, false, false), + Roles: []int64{6}, + IsMember: true, + ReadOnly: false, + + ShouldHaveAcces: false, + }, + + // ro roles - member access both roles + &TestCase{ + Name: "ro roles - member access both roles (ro)", + Conf: &models.CoreConfig{ + AllowedReadOnlyRoles: []int64{5, 6}, + }, + GWC: createUserGuild(true, false, false), + Roles: []int64{5, 6}, + IsMember: true, + ReadOnly: true, + + ShouldHaveAcces: true, + }, + &TestCase{ + Name: "ro roles - member access both roles", + Conf: &models.CoreConfig{ + AllowedReadOnlyRoles: []int64{5, 6}, + }, + GWC: createUserGuild(true, false, false), + Roles: []int64{5, 6}, + IsMember: true, + ReadOnly: false, + + ShouldHaveAcces: false, + }, + + // ro roles - admin access + &TestCase{ + Name: "ro roles-admin access (ro)", + Conf: &models.CoreConfig{ + AllowedReadOnlyRoles: []int64{5, 6}, + }, + GWC: createUserGuild(true, false, true), + Roles: nil, + IsMember: true, + ReadOnly: true, + + ShouldHaveAcces: true, + }, + &TestCase{ + Name: "ro roles-admin access", + Conf: &models.CoreConfig{ + AllowedReadOnlyRoles: []int64{5, 6}, + }, + GWC: createUserGuild(true, false, true), + Roles: nil, + IsMember: true, + ReadOnly: false, + + ShouldHaveAcces: true, + }, + + //////////////////////////////////// + // Write roles + //////////////////////////////////// + + // write roles - normal user access + &TestCase{ + Name: "write roles-normal user access (ro)", + Conf: &models.CoreConfig{ + AllowedWriteRoles: []int64{5, 6}, + }, + GWC: createUserGuild(true, false, false), + Roles: nil, + IsMember: false, + ReadOnly: true, + + ShouldHaveAcces: false, + }, + &TestCase{ + Name: "write roles-normal user access", + Conf: &models.CoreConfig{ + AllowedWriteRoles: []int64{5, 6}, + }, + GWC: createUserGuild(true, false, false), + Roles: nil, + IsMember: false, + ReadOnly: false, + + ShouldHaveAcces: false, + }, + + // write roles - member no roles + &TestCase{ + Name: "write roles-member no roles (ro)", + Conf: &models.CoreConfig{ + AllowedWriteRoles: []int64{5, 6}, + }, + GWC: createUserGuild(true, false, false), + Roles: nil, + IsMember: true, + ReadOnly: true, + + ShouldHaveAcces: false, + }, + &TestCase{ + Name: "write roles-member no roles", + Conf: &models.CoreConfig{ + AllowedWriteRoles: []int64{5, 6}, + }, + GWC: createUserGuild(true, false, false), + Roles: nil, + IsMember: true, + ReadOnly: false, + + ShouldHaveAcces: false, + }, + + // write roles - member access one role + &TestCase{ + Name: "write roles-member access one role (ro)", + Conf: &models.CoreConfig{ + AllowedWriteRoles: []int64{5, 6}, + }, + GWC: createUserGuild(true, false, false), + Roles: []int64{5}, + IsMember: true, + ReadOnly: true, + + ShouldHaveAcces: true, + }, + &TestCase{ + Name: "write roles-member access one role", + Conf: &models.CoreConfig{ + AllowedWriteRoles: []int64{5, 6}, + }, + GWC: createUserGuild(true, false, false), + Roles: []int64{5}, + IsMember: true, + ReadOnly: false, + + ShouldHaveAcces: true, + }, + + // write roles - member access other role + &TestCase{ + Name: "write roles-member access other role (ro)", + Conf: &models.CoreConfig{ + AllowedWriteRoles: []int64{5, 6}, + }, + GWC: createUserGuild(true, false, false), + Roles: []int64{6}, + IsMember: true, + ReadOnly: true, + + ShouldHaveAcces: true, + }, + &TestCase{ + Name: "write roles-member access other role", + Conf: &models.CoreConfig{ + AllowedWriteRoles: []int64{5, 6}, + }, + GWC: createUserGuild(true, false, false), + Roles: []int64{6}, + IsMember: true, + ReadOnly: false, + + ShouldHaveAcces: true, + }, + + // write roles - member access both roles + &TestCase{ + Name: "write roles - member access both roles (ro)", + Conf: &models.CoreConfig{ + AllowedWriteRoles: []int64{5, 6}, + }, + GWC: createUserGuild(true, false, false), + Roles: []int64{5, 6}, + IsMember: true, + ReadOnly: true, + + ShouldHaveAcces: true, + }, + &TestCase{ + Name: "write roles - member access both roles", + Conf: &models.CoreConfig{ + AllowedWriteRoles: []int64{5, 6}, + }, + GWC: createUserGuild(true, false, false), + Roles: []int64{5, 6}, + IsMember: true, + ReadOnly: false, + + ShouldHaveAcces: true, + }, + + // write roles - admin access + &TestCase{ + Name: "write roles-admin access (ro)", + Conf: &models.CoreConfig{ + AllowedWriteRoles: []int64{5, 6}, + }, + GWC: createUserGuild(true, false, true), + Roles: nil, + IsMember: true, + ReadOnly: true, + + ShouldHaveAcces: true, + }, + &TestCase{ + Name: "write roles-admin access", + Conf: &models.CoreConfig{ + AllowedWriteRoles: []int64{5, 6}, + }, + GWC: createUserGuild(true, false, true), + Roles: nil, + IsMember: true, + ReadOnly: false, + + ShouldHaveAcces: true, + }, + } + + for i, v := range testCases { + t.Run(fmt.Sprintf("Case #%d_%s", i, v.Name), func(it *testing.T) { + userID := int64(0) + if v.IsMember { + userID = 10 + } + + result := HasAccesstoGuildSettings(userID, v.GWC, v.Conf, StaticRoleProvider(v.Roles), !v.ReadOnly) + if result != v.ShouldHaveAcces { + it.Errorf("incorrect result, got %t, wanted: %t", result, v.ShouldHaveAcces) + } + }) + } + +}