mirror of
https://github.com/hrfee/jfa-go.git
synced 2026-01-18 16:47:42 +01:00
cache was being invalidated for every user, and on my 5000 user test instance, this sweated jellyseerr and my computer (audibly). Also, since this only needs to realistically run once, a flag is set in the database to indicate it's been done, and unset once the feature is disabled. It'll only run on boot if the flag is unset, or if triggered by the /tasks route. Will likely add manual trigger buttons on the web as well.
527 lines
19 KiB
Go
527 lines
19 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io/fs"
|
|
"net"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/hrfee/jfa-go/common"
|
|
"github.com/hrfee/jfa-go/easyproxy"
|
|
lm "github.com/hrfee/jfa-go/logmessages"
|
|
"gopkg.in/ini.v1"
|
|
)
|
|
|
|
type Config struct {
|
|
*ini.File
|
|
proxyTransport *http.Transport
|
|
proxyConfig *easyproxy.ProxyConfig
|
|
}
|
|
|
|
var emailEnabled = false
|
|
var messagesEnabled = false
|
|
var telegramEnabled = false
|
|
var discordEnabled = false
|
|
var matrixEnabled = false
|
|
|
|
// URL subpaths. Ignore the "Current" field, it's populated when in copies of the struct used for page templating.
|
|
// IMPORTANT: When linking straight to a page, rather than appending further to the URL (like accessing an API route), append a /.
|
|
var PAGES = PagePaths{}
|
|
|
|
func (config *Config) GetPath(sect, key string) (fs.FS, string) {
|
|
val := config.Section(sect).Key(key).MustString("")
|
|
if strings.HasPrefix(val, "jfa-go:") {
|
|
return localFS, strings.TrimPrefix(val, "jfa-go:")
|
|
}
|
|
dir, file := filepath.Split(val)
|
|
return os.DirFS(dir), file
|
|
}
|
|
|
|
func (config *Config) MustSetValue(section, key, val string) {
|
|
config.Section(section).Key(key).SetValue(config.Section(section).Key(key).MustString(val))
|
|
}
|
|
|
|
func (config *Config) MustSetURLPath(section, key, val string) {
|
|
if !strings.HasPrefix(val, "/") && val != "" {
|
|
val = "/" + val
|
|
}
|
|
config.MustSetValue(section, key, val)
|
|
}
|
|
|
|
func FixFullURL(v string) string {
|
|
// Keep relative paths relative
|
|
if strings.HasPrefix(v, "/") {
|
|
return v
|
|
}
|
|
if !strings.HasPrefix(v, "http://") && !strings.HasPrefix(v, "https://") {
|
|
v = "http://" + v
|
|
}
|
|
return v
|
|
}
|
|
|
|
func FormatSubpath(path string, removeSingleSlash bool) string {
|
|
if path == "/" {
|
|
if removeSingleSlash {
|
|
return ""
|
|
}
|
|
return path
|
|
}
|
|
return strings.TrimSuffix(path, "/")
|
|
}
|
|
|
|
func (config *Config) MustCorrectURL(section, key, value string) {
|
|
v := config.Section(section).Key(key).String()
|
|
if v == "" {
|
|
v = value
|
|
}
|
|
v = FixFullURL(v)
|
|
config.Section(section).Key(key).SetValue(v)
|
|
}
|
|
|
|
// ExternalDomain returns the Host for the request, using the fixed externalDomain value unless UseProxyHost is true.
|
|
func ExternalDomain(gc *gin.Context) string {
|
|
if !UseProxyHost || gc.Request.Host == "" {
|
|
return externalDomain
|
|
}
|
|
return gc.Request.Host
|
|
}
|
|
|
|
// ExternalDomainNoPort attempts to return ExternalDomain() with the port removed. If the internally-used method fails, it is assumed the domain has no port anyway.
|
|
func (app *appContext) ExternalDomainNoPort(gc *gin.Context) string {
|
|
domain := ExternalDomain(gc)
|
|
host, _, err := net.SplitHostPort(domain)
|
|
if err != nil {
|
|
return domain
|
|
}
|
|
return host
|
|
}
|
|
|
|
// ExternalURI returns the External URI of jfa-go's root directory (by default, where the admin page is), using the fixed externalURI value unless UseProxyHost is true and gc is not nil.
|
|
// When nil is passed, externalURI is returned.
|
|
func ExternalURI(gc *gin.Context) string {
|
|
if gc == nil {
|
|
return externalURI
|
|
}
|
|
|
|
var proto string
|
|
if gc.Request.TLS != nil || gc.Request.Header.Get("X-Forwarded-Proto") == "https" || gc.Request.Header.Get("X-Forwarded-Protocol") == "https" {
|
|
proto = "https://"
|
|
} else {
|
|
proto = "http://"
|
|
}
|
|
|
|
// app.debug.Printf("Request: %+v\n", gc.Request)
|
|
if UseProxyHost && gc.Request.Host != "" {
|
|
return proto + gc.Request.Host + PAGES.Base
|
|
}
|
|
return externalURI
|
|
}
|
|
|
|
func (app *appContext) EvaluateRelativePath(gc *gin.Context, path string) string {
|
|
if !strings.HasPrefix(path, "/") {
|
|
return path
|
|
}
|
|
|
|
var proto string
|
|
if gc.Request.TLS != nil || gc.Request.Header.Get("X-Forwarded-Proto") == "https" || gc.Request.Header.Get("X-Forwarded-Protocol") == "https" {
|
|
proto = "https://"
|
|
} else {
|
|
proto = "http://"
|
|
}
|
|
|
|
return proto + ExternalDomain(gc) + path
|
|
}
|
|
|
|
// NewConfig reads and patches a config file for use. Passed loggers are used only once. Some dependencies can be reloaded after this is called with ReloadDependents(app).
|
|
func NewConfig(configPathOrContents any, dataPath string, logs LoggerSet) (*Config, error) {
|
|
var err error
|
|
config := &Config{}
|
|
config.File, err = ini.ShadowLoad(configPathOrContents)
|
|
if err != nil {
|
|
return config, err
|
|
}
|
|
|
|
// URLs
|
|
config.MustSetURLPath("ui", "url_base", "")
|
|
config.MustSetURLPath("url_paths", "admin", "")
|
|
config.MustSetURLPath("url_paths", "user_page", "/my/account")
|
|
config.MustSetURLPath("url_paths", "form", "/invite")
|
|
PAGES.Base = FormatSubpath(config.Section("ui").Key("url_base").String(), true)
|
|
PAGES.Admin = FormatSubpath(config.Section("url_paths").Key("admin").String(), true)
|
|
PAGES.MyAccount = FormatSubpath(config.Section("url_paths").Key("user_page").String(), true)
|
|
PAGES.Form = FormatSubpath(config.Section("url_paths").Key("form").String(), true)
|
|
if !(config.Section("user_page").Key("enabled").MustBool(true)) {
|
|
PAGES.MyAccount = "disabled"
|
|
}
|
|
if PAGES.Base == PAGES.Form || PAGES.Base == "/accounts" || PAGES.Base == "/settings" || PAGES.Base == "/activity" {
|
|
logs.err.Printf(lm.BadURLBase, PAGES.Base)
|
|
}
|
|
logs.info.Printf(lm.SubpathBlockMessage, PAGES.Base, PAGES.Admin, PAGES.MyAccount, PAGES.Form)
|
|
|
|
config.MustCorrectURL("jellyfin", "server", "")
|
|
config.MustCorrectURL("jellyfin", "public_server", config.Section("jellyfin").Key("server").String())
|
|
config.MustCorrectURL("ui", "redirect_url", config.Section("jellyfin").Key("public_server").String())
|
|
|
|
for _, key := range config.Section("files").Keys() {
|
|
if name := key.Name(); name != "html_templates" && name != "lang_files" {
|
|
key.SetValue(key.MustString(filepath.Join(dataPath, (key.Name() + ".json"))))
|
|
}
|
|
}
|
|
for _, key := range []string{"user_configuration", "user_displayprefs", "user_profiles", "ombi_template", "invites", "emails", "user_template", "custom_emails", "users", "telegram_users", "discord_users", "matrix_users", "announcements", "custom_user_page_content"} {
|
|
config.Section("files").Key(key).SetValue(config.Section("files").Key(key).MustString(filepath.Join(dataPath, (key + ".json"))))
|
|
}
|
|
for _, key := range []string{"matrix_sql"} {
|
|
config.Section("files").Key(key).SetValue(config.Section("files").Key(key).MustString(filepath.Join(dataPath, (key + ".db"))))
|
|
}
|
|
|
|
// If true, ExternalDomain() will return one based on the reported Host (ideally reported in "Host" or "X-Forwarded-Host" by the reverse proxy), falling back to externalDomain if not set.
|
|
UseProxyHost = config.Section("ui").Key("use_proxy_host").MustBool(false)
|
|
externalURI = strings.TrimSuffix(strings.TrimSuffix(config.Section("ui").Key("jfa_url").MustString(""), "/invite"), "/")
|
|
if !strings.HasSuffix(externalURI, PAGES.Base) {
|
|
logs.err.Println(lm.NoURLSuffix)
|
|
}
|
|
if externalURI == "" {
|
|
if UseProxyHost {
|
|
logs.err.Println(lm.NoExternalHost + lm.LoginWontSave + lm.SetExternalHostDespiteUseProxyHost)
|
|
} else {
|
|
logs.err.Println(lm.NoExternalHost + lm.LoginWontSave)
|
|
}
|
|
}
|
|
u, err := url.Parse(externalURI)
|
|
if err == nil {
|
|
externalDomain = u.Hostname()
|
|
}
|
|
|
|
config.Section("email").Key("no_username").SetValue(strconv.FormatBool(config.Section("email").Key("no_username").MustBool(false)))
|
|
|
|
// FIXME: Remove all these, eventually
|
|
// config.MustSetValue("password_resets", "email_html", "jfa-go:"+"email.html")
|
|
// config.MustSetValue("password_resets", "email_text", "jfa-go:"+"email.txt")
|
|
|
|
// config.MustSetValue("invite_emails", "email_html", "jfa-go:"+"invite-email.html")
|
|
// config.MustSetValue("invite_emails", "email_text", "jfa-go:"+"invite-email.txt")
|
|
|
|
// config.MustSetValue("email_confirmation", "email_html", "jfa-go:"+"confirmation.html")
|
|
// config.MustSetValue("email_confirmation", "email_text", "jfa-go:"+"confirmation.txt")
|
|
|
|
// config.MustSetValue("notifications", "expiry_html", "jfa-go:"+"expired.html")
|
|
// config.MustSetValue("notifications", "expiry_text", "jfa-go:"+"expired.txt")
|
|
|
|
// config.MustSetValue("notifications", "created_html", "jfa-go:"+"created.html")
|
|
// config.MustSetValue("notifications", "created_text", "jfa-go:"+"created.txt")
|
|
|
|
// config.MustSetValue("deletion", "email_html", "jfa-go:"+"deleted.html")
|
|
// config.MustSetValue("deletion", "email_text", "jfa-go:"+"deleted.txt")
|
|
|
|
// Deletion template is good enough for these as well.
|
|
// config.MustSetValue("disable_enable", "disabled_html", "jfa-go:"+"deleted.html")
|
|
// config.MustSetValue("disable_enable", "disabled_text", "jfa-go:"+"deleted.txt")
|
|
// config.MustSetValue("disable_enable", "enabled_html", "jfa-go:"+"deleted.html")
|
|
// config.MustSetValue("disable_enable", "enabled_text", "jfa-go:"+"deleted.txt")
|
|
|
|
// config.MustSetValue("welcome_email", "email_html", "jfa-go:"+"welcome.html")
|
|
// config.MustSetValue("welcome_email", "email_text", "jfa-go:"+"welcome.txt")
|
|
|
|
// config.MustSetValue("template_email", "email_html", "jfa-go:"+"template.html")
|
|
// config.MustSetValue("template_email", "email_text", "jfa-go:"+"template.txt")
|
|
|
|
config.MustSetValue("user_expiry", "behaviour", "disable_user")
|
|
// config.MustSetValue("user_expiry", "email_html", "jfa-go:"+"user-expired.html")
|
|
// config.MustSetValue("user_expiry", "email_text", "jfa-go:"+"user-expired.txt")
|
|
|
|
// config.MustSetValue("user_expiry", "adjustment_email_html", "jfa-go:"+"expiry-adjusted.html")
|
|
// config.MustSetValue("user_expiry", "adjustment_email_text", "jfa-go:"+"expiry-adjusted.txt")
|
|
|
|
// config.MustSetValue("user_expiry", "reminder_email_html", "jfa-go:"+"expiry-reminder.html")
|
|
// config.MustSetValue("user_expiry", "reminder_email_text", "jfa-go:"+"expiry-reminder.txt")
|
|
|
|
fnameSettingSuffix := []string{"html", "text"}
|
|
fnameExtension := []string{"html", "txt"}
|
|
|
|
for _, cc := range customContent {
|
|
if cc.SourceFile.DefaultValue == "" {
|
|
continue
|
|
}
|
|
for i := range fnameSettingSuffix {
|
|
config.MustSetValue(cc.SourceFile.Section, cc.SourceFile.SettingPrefix+fnameSettingSuffix[i], "jfa-go:"+cc.SourceFile.DefaultValue+"."+fnameExtension[i])
|
|
}
|
|
}
|
|
|
|
config.MustSetValue("smtp", "hello_hostname", "localhost")
|
|
config.MustSetValue("smtp", "cert_validation", "true")
|
|
config.MustSetValue("smtp", "auth_type", "4")
|
|
config.MustSetValue("smtp", "port", "465")
|
|
|
|
config.MustSetValue("activity_log", "keep_n_records", "1000")
|
|
config.MustSetValue("activity_log", "delete_after_days", "90")
|
|
|
|
sc := config.Section("discord").Key("start_command").MustString("start")
|
|
config.Section("discord").Key("start_command").SetValue(strings.TrimPrefix(strings.TrimPrefix(sc, "/"), "!"))
|
|
|
|
config.MustSetValue("email", "collect", "true")
|
|
collect := config.Section("email").Key("collect").MustBool(true)
|
|
required := config.Section("email").Key("required").MustBool(false) && collect
|
|
config.Section("email").Key("required").SetValue(strconv.FormatBool(required))
|
|
unique := config.Section("email").Key("require_unique").MustBool(false) && collect
|
|
config.Section("email").Key("require_unique").SetValue(strconv.FormatBool(unique))
|
|
|
|
config.MustSetValue("matrix", "topic", "Jellyfin notifications")
|
|
config.MustSetValue("matrix", "show_on_reg", "true")
|
|
|
|
config.MustSetValue("discord", "show_on_reg", "true")
|
|
|
|
config.MustSetValue("telegram", "show_on_reg", "true")
|
|
|
|
config.MustSetValue("backups", "every_n_minutes", "1440")
|
|
config.MustSetValue("backups", "path", filepath.Join(dataPath, "backups"))
|
|
config.MustSetValue("backups", "keep_n_backups", "20")
|
|
config.MustSetValue("backups", "keep_previous_version_backup", "true")
|
|
|
|
config.Section("jellyfin").Key("version").SetValue(version)
|
|
config.Section("jellyfin").Key("device").SetValue("jfa-go")
|
|
config.Section("jellyfin").Key("device_id").SetValue(fmt.Sprintf("jfa-go-%s-%s", version, commit))
|
|
|
|
config.MustSetValue("jellyfin", "cache_timeout", "30")
|
|
config.MustSetValue("jellyfin", "web_cache_async_timeout", "1")
|
|
config.MustSetValue("jellyfin", "web_cache_sync_timeout", "10")
|
|
|
|
LOGIP = config.Section("advanced").Key("log_ips").MustBool(false)
|
|
LOGIPU = config.Section("advanced").Key("log_ips_users").MustBool(false)
|
|
|
|
config.MustSetValue("advanced", "auth_retry_count", "6")
|
|
config.MustSetValue("advanced", "auth_retry_gap", "10")
|
|
|
|
config.MustSetValue("ui", "port", "8056")
|
|
config.MustSetValue("advanced", "tls_port", "8057")
|
|
|
|
config.MustSetValue("advanced", "value_log_size", "512")
|
|
|
|
pwrMethods := []string{"allow_pwr_username", "allow_pwr_email", "allow_pwr_contact_method"}
|
|
allDisabled := true
|
|
for _, v := range pwrMethods {
|
|
if config.Section("user_page").Key(v).MustBool(true) {
|
|
allDisabled = false
|
|
}
|
|
}
|
|
if allDisabled {
|
|
logs.info.Println(lm.EnableAllPWRMethods)
|
|
for _, v := range pwrMethods {
|
|
config.Section("user_page").Key(v).SetValue("true")
|
|
}
|
|
}
|
|
|
|
messagesEnabled = config.Section("messages").Key("enabled").MustBool(false)
|
|
telegramEnabled = config.Section("telegram").Key("enabled").MustBool(false)
|
|
discordEnabled = config.Section("discord").Key("enabled").MustBool(false)
|
|
matrixEnabled = config.Section("matrix").Key("enabled").MustBool(false)
|
|
if !messagesEnabled {
|
|
emailEnabled = false
|
|
telegramEnabled = false
|
|
discordEnabled = false
|
|
matrixEnabled = false
|
|
} else if config.Section("email").Key("method").MustString("") == "" {
|
|
emailEnabled = false
|
|
} else {
|
|
emailEnabled = true
|
|
}
|
|
if !emailEnabled && !telegramEnabled && !discordEnabled && !matrixEnabled {
|
|
messagesEnabled = false
|
|
}
|
|
|
|
if proxyEnabled := config.Section("advanced").Key("proxy").MustBool(false); proxyEnabled {
|
|
config.proxyConfig = &easyproxy.ProxyConfig{}
|
|
config.proxyConfig.Protocol = easyproxy.HTTP
|
|
if strings.Contains(config.Section("advanced").Key("proxy_protocol").MustString("http"), "socks") {
|
|
config.proxyConfig.Protocol = easyproxy.SOCKS5
|
|
}
|
|
config.proxyConfig.Addr = config.Section("advanced").Key("proxy_address").MustString("")
|
|
config.proxyConfig.User = config.Section("advanced").Key("proxy_user").MustString("")
|
|
config.proxyConfig.Password = config.Section("advanced").Key("proxy_password").MustString("")
|
|
config.proxyTransport, err = easyproxy.NewTransport(*(config.proxyConfig))
|
|
if err != nil {
|
|
logs.err.Printf(lm.FailedInitProxy, config.proxyConfig.Addr, err)
|
|
// As explained in lm.FailedInitProxy, sleep here might grab the admin's attention,
|
|
// Since we don't crash on this failing.
|
|
time.Sleep(15 * time.Second)
|
|
config.proxyConfig = nil
|
|
config.proxyTransport = nil
|
|
} else {
|
|
logs.info.Printf(lm.InitProxy, config.proxyConfig.Addr)
|
|
}
|
|
}
|
|
|
|
config.MustSetValue("updates", "enabled", "true")
|
|
|
|
substituteStrings = config.Section("jellyfin").Key("substitute_jellyfin_strings").MustString("")
|
|
|
|
if substituteStrings != "" {
|
|
v := config.Section("ui").Key("success_message")
|
|
v.SetValue(strings.ReplaceAll(v.String(), "Jellyfin", substituteStrings))
|
|
}
|
|
|
|
datePattern = config.Section("messages").Key("date_format").String()
|
|
timePattern = `%H:%M`
|
|
if !(config.Section("messages").Key("use_24h").MustBool(true)) {
|
|
timePattern = `%I:%M %p`
|
|
}
|
|
|
|
return config, nil
|
|
}
|
|
|
|
// ReloadDependents re-initialises or applies changes to components of the app which can be reconfigured without restarting.
|
|
func (config *Config) ReloadDependents(app *appContext) {
|
|
oldFormLang := config.Section("ui").Key("language").MustString("")
|
|
if oldFormLang != "" {
|
|
app.storage.lang.chosenUserLang = oldFormLang
|
|
}
|
|
newFormLang := config.Section("ui").Key("language-form").MustString("")
|
|
if newFormLang != "" {
|
|
app.storage.lang.chosenUserLang = newFormLang
|
|
}
|
|
|
|
app.storage.lang.chosenAdminLang = config.Section("ui").Key("language-admin").MustString("en-us")
|
|
app.storage.lang.chosenEmailLang = config.Section("email").Key("language").MustString("en-us")
|
|
app.storage.lang.chosenPWRLang = config.Section("password_resets").Key("language").MustString("en-us")
|
|
app.storage.lang.chosenTelegramLang = config.Section("telegram").Key("language").MustString("en-us")
|
|
|
|
releaseChannel := config.Section("updates").Key("channel").String()
|
|
if config.Section("updates").Key("enabled").MustBool(false) {
|
|
v := version
|
|
if releaseChannel == "stable" {
|
|
if version == "git" {
|
|
v = "0.0.0"
|
|
}
|
|
} else if releaseChannel == "unstable" {
|
|
v = "git"
|
|
}
|
|
app.updater = NewUpdater(baseURL, namespace, repo, v, commit, updater)
|
|
if config.proxyTransport != nil {
|
|
app.updater.SetTransport(config.proxyTransport)
|
|
}
|
|
}
|
|
if releaseChannel == "" {
|
|
if version == "git" {
|
|
releaseChannel = "unstable"
|
|
} else {
|
|
releaseChannel = "stable"
|
|
}
|
|
config.MustSetValue("updates", "channel", releaseChannel)
|
|
}
|
|
|
|
app.email = NewEmailer(config, app.storage, app.LoggerSet)
|
|
|
|
}
|
|
|
|
func (app *appContext) ReloadConfig() {
|
|
var err error = nil
|
|
app.config, err = NewConfig(app.configPath, app.dataPath, app.LoggerSet)
|
|
if err != nil {
|
|
app.err.Fatalf(lm.FailedLoadConfig, app.configPath, err)
|
|
}
|
|
|
|
app.config.ReloadDependents(app)
|
|
app.info.Printf(lm.LoadConfig, app.configPath)
|
|
}
|
|
|
|
func (app *appContext) PatchConfigBase() {
|
|
conf := app.configBase
|
|
// Load language options
|
|
formOptions := app.storage.lang.User.getOptions()
|
|
pwrOptions := app.storage.lang.PasswordReset.getOptions()
|
|
adminOptions := app.storage.lang.Admin.getOptions()
|
|
emailOptions := app.storage.lang.Email.getOptions()
|
|
telegramOptions := app.storage.lang.Email.getOptions()
|
|
|
|
for i, section := range app.configBase.Sections {
|
|
if section.Section == "updates" && updater == "" {
|
|
section.Meta.Disabled = true
|
|
}
|
|
for j, setting := range section.Settings {
|
|
if section.Section == "ui" {
|
|
if setting.Setting == "language-form" {
|
|
setting.Options = formOptions
|
|
setting.Value = "en-us"
|
|
} else if setting.Setting == "language-admin" {
|
|
setting.Options = adminOptions
|
|
setting.Value = "en-us"
|
|
}
|
|
} else if section.Section == "password_resets" {
|
|
if setting.Setting == "language" {
|
|
setting.Options = pwrOptions
|
|
setting.Value = "en-us"
|
|
}
|
|
} else if section.Section == "email" {
|
|
if setting.Setting == "language" {
|
|
setting.Options = emailOptions
|
|
setting.Value = "en-us"
|
|
}
|
|
} else if section.Section == "telegram" {
|
|
if setting.Setting == "language" {
|
|
setting.Options = telegramOptions
|
|
setting.Value = "en-us"
|
|
}
|
|
} else if section.Section == "smtp" {
|
|
if setting.Setting == "ssl_cert" && PLATFORM == "windows" {
|
|
// Not accurate but the effect is hiding the option, which we want.
|
|
setting.Deprecated = true
|
|
}
|
|
} else if section.Section == "matrix" {
|
|
if setting.Setting == "encryption" && !MatrixE2EE() {
|
|
// Not accurate but the effect is hiding the option, which we want.
|
|
setting.Deprecated = true
|
|
}
|
|
}
|
|
val := app.config.Section(section.Section).Key(setting.Setting)
|
|
switch setting.Type {
|
|
case "list":
|
|
setting.Value = val.StringsWithShadows("|")
|
|
case "text", "email", "select", "password", "note":
|
|
setting.Value = val.MustString("")
|
|
case "number":
|
|
setting.Value = val.MustInt(0)
|
|
case "bool":
|
|
setting.Value = val.MustBool(false)
|
|
}
|
|
section.Settings[j] = setting
|
|
}
|
|
conf.Sections[i] = section
|
|
}
|
|
app.patchedConfig = conf
|
|
}
|
|
|
|
func (app *appContext) PatchConfigDiscordRoles() {
|
|
if !discordEnabled {
|
|
return
|
|
}
|
|
r, err := app.discord.ListRoles()
|
|
if err != nil {
|
|
return
|
|
}
|
|
roles := make([]common.Option, len(r)+1)
|
|
roles[0] = common.Option{"", "None"}
|
|
for i, role := range r {
|
|
roles[i+1] = role
|
|
}
|
|
|
|
for i, section := range app.patchedConfig.Sections {
|
|
if section.Section != "discord" {
|
|
continue
|
|
}
|
|
for j, setting := range section.Settings {
|
|
if setting.Setting != "apply_role" {
|
|
continue
|
|
}
|
|
setting.Options = roles
|
|
section.Settings[j] = setting
|
|
}
|
|
app.patchedConfig.Sections[i] = section
|
|
}
|
|
}
|