Initial commit: docker compose config
Release Docker multi arch / docker (push) Has been cancelled
Test Install Script / Test Script Syntax (push) Has been cancelled
Test Install Script / Test on almalinux-10 (default) (push) Has been cancelled
Test Install Script / Test on almalinux-10 (root) (push) Has been cancelled
Test Install Script / Test on almalinux-8 (default) (push) Has been cancelled
Test Install Script / Test on almalinux-8 (root) (push) Has been cancelled
Test Install Script / Test on almalinux-9 (default) (push) Has been cancelled
Test Install Script / Test on almalinux-9 (root) (push) Has been cancelled
Test Install Script / Test on amazonlinux-2 (default) (push) Has been cancelled
Test Install Script / Test on amazonlinux-2 (root) (push) Has been cancelled
Test Install Script / Test on debian-11 (default) (push) Has been cancelled
Test Install Script / Test on debian-11 (root) (push) Has been cancelled
Test Install Script / Test on debian-12 (default) (push) Has been cancelled
Test Install Script / Test on debian-12 (root) (push) Has been cancelled
Test Install Script / Test on debian-13 (default) (push) Has been cancelled
Test Install Script / Test on debian-13 (root) (push) Has been cancelled
Test Install Script / Test on fedora-latest (default) (push) Has been cancelled
Test Install Script / Test on fedora-latest (root) (push) Has been cancelled
Test Install Script / Test on rocky-10 (default) (push) Has been cancelled
Test Install Script / Test on rocky-10 (root) (push) Has been cancelled
Test Install Script / Test on rocky-8 (default) (push) Has been cancelled
Test Install Script / Test on rocky-8 (root) (push) Has been cancelled
Test Install Script / Test on rocky-9 (default) (push) Has been cancelled
Test Install Script / Test on rocky-9 (root) (push) Has been cancelled
Test Install Script / Test on ubuntu-22.04 (default) (push) Has been cancelled
Test Install Script / Test on ubuntu-22.04 (root) (push) Has been cancelled
Test Install Script / Test on ubuntu-24.04 (default) (push) Has been cancelled
Test Install Script / Test on ubuntu-24.04 (root) (push) Has been cancelled

This commit is contained in:
2026-05-30 12:07:11 +00:00
commit 616c6b1c62
381 changed files with 55145 additions and 0 deletions
+654
View File
@@ -0,0 +1,654 @@
package tgbot
import (
"bytes"
"encoding/json"
"fmt"
"strings"
tele "gopkg.in/telebot.v4"
"server/dlna"
"server/rutor"
"server/settings"
"server/torr"
)
func cmdSettings(c tele.Context) error {
uid := c.Sender().ID
if settings.BTsets == nil {
return c.Send(tr(uid, "settings_not_loaded"))
}
return sendSettingsMenu(c, uid)
}
func sendSettingsMenu(c tele.Context, uid int64) error {
return sendSettingsMenuPage(c, uid, "1")
}
func sendSettingsMenuPage(c tele.Context, uid int64, page string) error {
msg := sendSettingsMenuText(c, uid, page)
kbd := sendSettingsMenuKbd(uid, page)
return c.Send(msg, kbd)
}
func sendSettingsMenuText(c tele.Context, uid int64, page string) string {
s := settings.BTsets
msg := "⚙️ <b>" + tr(uid, "settings_title") + "</b>"
switch page {
case "1":
msg += "\n\n"
msg += fmt.Sprintf("🔍 %s: RuTor %s · Torznab %s\n", tr(uid, "settings_section_search"), boolIcon(s.EnableRutorSearch), boolIcon(s.EnableTorznabSearch))
msg += fmt.Sprintf("📺 %s: DLNA %s · IPv6 %s · DHT %s · PEX %s · TCP %s · UTP %s\n", tr(uid, "settings_section_network"), boolIcon(s.EnableDLNA), boolIcon(s.EnableIPv6), boolIcon(!s.DisableDHT), boolIcon(!s.DisablePEX), boolIcon(!s.DisableTCP), boolIcon(!s.DisableUTP))
msg += fmt.Sprintf("📦 %s: CacheDrop %s · Proxy %s · UseDisk %s\n", tr(uid, "settings_section_other"), boolIcon(s.RemoveCacheOnDrop), boolIcon(s.EnableProxy), boolIcon(s.UseDisk))
case "1a":
msg += " — " + tr(uid, "settings_section_search")
msg += "\n\n"
msg += fmt.Sprintf("RuTor %s · Torznab %s", boolIcon(s.EnableRutorSearch), boolIcon(s.EnableTorznabSearch))
case "1b":
msg += " — " + tr(uid, "settings_section_network")
msg += "\n\n"
msg += fmt.Sprintf("DLNA %s · IPv6 %s · Upload %s · DHT %s · PEX %s\n", boolIcon(s.EnableDLNA), boolIcon(s.EnableIPv6), boolIcon(!s.DisableUpload), boolIcon(!s.DisableDHT), boolIcon(!s.DisablePEX))
msg += fmt.Sprintf("TCP %s · UTP %s · UPNP %s · Encrypt %s · Debug %s", boolIcon(!s.DisableTCP), boolIcon(!s.DisableUTP), boolIcon(!s.DisableUPNP), boolIcon(s.ForceEncrypt), boolIcon(s.EnableDebug))
case "1c":
msg += " — " + tr(uid, "settings_section_other")
msg += "\n\n"
msg += fmt.Sprintf("CacheDrop %s · Responsive %s · Proxy %s · UseDisk %s · FSActive %s", boolIcon(s.RemoveCacheOnDrop), boolIcon(s.ResponsiveMode), boolIcon(s.EnableProxy), boolIcon(s.UseDisk), boolIcon(s.ShowFSActiveTorr))
case "2":
msg += " — " + tr(uid, "settings_page2")
msg += "\n\n"
msg += fmt.Sprintf("💾 %s: %d MB · Preload %d%% · ReadAhead %d%%\n", tr(uid, "settings_limits_cache"), s.CacheSize/(1024*1024), s.PreloadCache, s.ReaderReadAHead)
msg += fmt.Sprintf("🔌 %s: %d · Port %s · Timeout %ds\n", tr(uid, "settings_limits_connections"), s.ConnectionsLimit, portStr(s.PeersListenPort), s.TorrentDisconnectTimeout)
msg += fmt.Sprintf("⬇️ %s: Down %s · Up %s · Retr %s\n", tr(uid, "settings_limits_speed"), rateStr(s.DownloadRateLimit), rateStr(s.UploadRateLimit), retrackersStr(s.RetrackersMode))
case "2a":
msg += " — " + tr(uid, "settings_page2") + " · " + tr(uid, "settings_limits_cache")
msg += "\n\n"
msg += fmt.Sprintf("Cache %d MB · Preload %d%% · ReadAhead %d%%", s.CacheSize/(1024*1024), s.PreloadCache, s.ReaderReadAHead)
case "2b":
msg += " — " + tr(uid, "settings_page2") + " · " + tr(uid, "settings_limits_connections")
msg += "\n\n"
msg += fmt.Sprintf("Connections %d · Port %s · Timeout %ds", s.ConnectionsLimit, portStr(s.PeersListenPort), s.TorrentDisconnectTimeout)
case "2c":
msg += " — " + tr(uid, "settings_page2") + " · " + tr(uid, "settings_limits_speed")
msg += "\n\n"
msg += fmt.Sprintf("Down %s · Up %s · Retrackers %s", rateStr(s.DownloadRateLimit), rateStr(s.UploadRateLimit), retrackersStr(s.RetrackersMode))
case "3":
msg += " — " + tr(uid, "settings_page3")
msg += "\n\n"
msg += fmt.Sprintf("📺 DLNA: %s · 💾 Path: %s\n", maskStr(s.FriendlyName, 25), maskVal(s.TorrentsSavePath))
msg += fmt.Sprintf("🔐 SSL: %s · 🔑 TMDB: %s · Torznab: %d\n", maskVal(s.SslCert), maskVal(s.TMDBSettings.APIKey), len(s.TorznabUrls))
msg += fmt.Sprintf("🌐 Proxy: %s", maskStr(strings.Join(s.ProxyHosts, ", "), 35))
case "4":
msg += " — " + tr(uid, "settings_page4")
msg += "\n\n"
msg += fmt.Sprintf("📄 %s: %s · 📺 %s: %s\n", tr(uid, "settings_storage_settings"), storageType(s.StoreSettingsInJson), tr(uid, "settings_storage_viewed"), storageType(s.StoreViewedInJson))
msg += fmt.Sprintf("🔑 TMDB: %s · 🖼 URL: %s", maskVal(s.TMDBSettings.APIKey), maskStr(s.TMDBSettings.ImageURL, 20))
}
return msg
}
func storageType(useJSON bool) string {
if useJSON {
return "json"
}
return "bbolt"
}
func rateStr(kb int) string {
if kb == 0 {
return "∞"
}
return fmt.Sprintf("%d", kb)
}
func portStr(port int) string {
if port == 0 {
return "auto"
}
return fmt.Sprintf("%d", port)
}
func maskStr(s string, maxLen int) string {
if s == "" {
return "—"
}
if len(s) > maxLen {
return s[:maxLen-3] + "..."
}
return s
}
func maskVal(s string) string {
if s == "" {
return "—"
}
return "***"
}
func retrackersStr(mode int) string {
switch mode {
case 0:
return "off"
case 1:
return "add"
case 2:
return "remove"
case 3:
return "replace"
default:
return "?"
}
}
func sendSettingsMenuKbd(uid int64, page string) *tele.ReplyMarkup {
s := settings.BTsets
var btns [][]tele.InlineButton
switch page {
case "1":
btns = [][]tele.InlineButton{
{
{Text: "🔍 " + tr(uid, "settings_section_search"), Unique: "fset", Data: "page|1a"},
{Text: "📺 " + tr(uid, "settings_section_network"), Unique: "fset", Data: "page|1b"},
{Text: "📦 " + tr(uid, "settings_section_other"), Unique: "fset", Data: "page|1c"},
},
{
{Text: "📥 " + tr(uid, "settings_export"), Unique: "fset", Data: "export"},
{Text: "📊 " + tr(uid, "settings_nav_cache"), Unique: "fset", Data: "page|2"},
{Text: "✏️ " + tr(uid, "settings_nav_paths"), Unique: "fset", Data: "page|3"},
{Text: "💾 " + tr(uid, "settings_nav_storage"), Unique: "fset", Data: "page|4"},
},
}
case "1a":
btns = [][]tele.InlineButton{
{
{Text: "◀️ " + tr(uid, "settings_back"), Unique: "fset", Data: "page|1"},
},
{
{Text: toggleBtn("RuTor", s.EnableRutorSearch), Unique: "fset", Data: "rutor|1a"},
{Text: toggleBtn("Torznab", s.EnableTorznabSearch), Unique: "fset", Data: "torznab|1a"},
},
}
case "1b":
btns = [][]tele.InlineButton{
{
{Text: "◀️ " + tr(uid, "settings_back"), Unique: "fset", Data: "page|1"},
},
{
{Text: toggleBtn("DLNA", s.EnableDLNA), Unique: "fset", Data: "dlna|1b"},
{Text: toggleBtn("IPv6", s.EnableIPv6), Unique: "fset", Data: "ipv6|1b"},
{Text: toggleBtn("Upload", !s.DisableUpload), Unique: "fset", Data: "upload|1b"},
},
{
{Text: toggleBtn("DHT", !s.DisableDHT), Unique: "fset", Data: "dht|1b"},
{Text: toggleBtn("PEX", !s.DisablePEX), Unique: "fset", Data: "pex|1b"},
{Text: toggleBtn("TCP", !s.DisableTCP), Unique: "fset", Data: "tcp|1b"},
{Text: toggleBtn("UTP", !s.DisableUTP), Unique: "fset", Data: "utp|1b"},
},
{
{Text: toggleBtn("UPNP", !s.DisableUPNP), Unique: "fset", Data: "upnp|1b"},
{Text: toggleBtn("Encrypt", s.ForceEncrypt), Unique: "fset", Data: "encrypt|1b"},
{Text: toggleBtn("Debug", s.EnableDebug), Unique: "fset", Data: "debug|1b"},
},
}
case "1c":
btns = [][]tele.InlineButton{
{
{Text: "◀️ " + tr(uid, "settings_back"), Unique: "fset", Data: "page|1"},
},
{
{Text: toggleBtn("CacheDrop", s.RemoveCacheOnDrop), Unique: "fset", Data: "cachedrop|1c"},
{Text: toggleBtn("Responsive", s.ResponsiveMode), Unique: "fset", Data: "responsive|1c"},
{Text: toggleBtn("Proxy", s.EnableProxy), Unique: "fset", Data: "proxy|1c"},
},
{
{Text: toggleBtn("UseDisk", s.UseDisk), Unique: "fset", Data: "usedisk|1c"},
{Text: toggleBtn("FSActive", s.ShowFSActiveTorr), Unique: "fset", Data: "fsactive|1c"},
},
}
case "2":
btns = [][]tele.InlineButton{
{
{Text: "💾 " + tr(uid, "settings_limits_cache"), Unique: "fset", Data: "page|2a"},
{Text: "🔌 " + tr(uid, "settings_limits_connections"), Unique: "fset", Data: "page|2b"},
{Text: "⬇️ " + tr(uid, "settings_limits_speed"), Unique: "fset", Data: "page|2c"},
},
{
{Text: "◀️ " + tr(uid, "settings_back"), Unique: "fset", Data: "page|1"},
{Text: "✏️ " + tr(uid, "settings_nav_paths"), Unique: "fset", Data: "page|3"},
{Text: "💾 " + tr(uid, "settings_nav_storage"), Unique: "fset", Data: "page|4"},
},
}
case "2a":
cacheMB := int(s.CacheSize / (1024 * 1024))
btns = [][]tele.InlineButton{
{
{Text: "◀️ " + tr(uid, "settings_back"), Unique: "fset", Data: "page|2"},
},
{
{Text: "💾 " + optBtn("64", cacheMB == 64), Unique: "fset", Data: "cache|64|2a"},
{Text: optBtn("128", cacheMB == 128), Unique: "fset", Data: "cache|128|2a"},
{Text: optBtn("256", cacheMB == 256), Unique: "fset", Data: "cache|256|2a"},
{Text: optBtn("512", cacheMB == 512), Unique: "fset", Data: "cache|512|2a"},
},
{
{Text: "📥 " + optBtn("25%", s.PreloadCache == 25), Unique: "fset", Data: "preload|25|2a"},
{Text: optBtn("50%", s.PreloadCache == 50), Unique: "fset", Data: "preload|50|2a"},
{Text: optBtn("75%", s.PreloadCache == 75), Unique: "fset", Data: "preload|75|2a"},
{Text: optBtn("95%", s.PreloadCache == 95), Unique: "fset", Data: "preload|95|2a"},
},
{
{Text: "📖 " + optBtn("50%", s.ReaderReadAHead == 50), Unique: "fset", Data: "readahead|50|2a"},
{Text: optBtn("75%", s.ReaderReadAHead == 75), Unique: "fset", Data: "readahead|75|2a"},
{Text: optBtn("95%", s.ReaderReadAHead == 95), Unique: "fset", Data: "readahead|95|2a"},
{Text: optBtn("100%", s.ReaderReadAHead == 100), Unique: "fset", Data: "readahead|100|2a"},
},
}
case "2b":
btns = [][]tele.InlineButton{
{
{Text: "◀️ " + tr(uid, "settings_back"), Unique: "fset", Data: "page|2"},
},
{
{Text: "🔌 " + optBtn("25", s.ConnectionsLimit == 25), Unique: "fset", Data: "conn|25|2b"},
{Text: optBtn("50", s.ConnectionsLimit == 50), Unique: "fset", Data: "conn|50|2b"},
{Text: optBtn("100", s.ConnectionsLimit == 100), Unique: "fset", Data: "conn|100|2b"},
},
{
{Text: "⏱ " + optBtn("15s", s.TorrentDisconnectTimeout == 15), Unique: "fset", Data: "timeout|15|2b"},
{Text: optBtn("30s", s.TorrentDisconnectTimeout == 30), Unique: "fset", Data: "timeout|30|2b"},
{Text: optBtn("60s", s.TorrentDisconnectTimeout == 60), Unique: "fset", Data: "timeout|60|2b"},
{Text: optBtn("120s", s.TorrentDisconnectTimeout == 120), Unique: "fset", Data: "timeout|120|2b"},
},
{
{Text: "🔌 " + optBtn("auto", s.PeersListenPort == 0), Unique: "fset", Data: "port|0|2b"},
{Text: optBtn("6881", s.PeersListenPort == 6881), Unique: "fset", Data: "port|6881|2b"},
{Text: optBtn("51413", s.PeersListenPort == 51413), Unique: "fset", Data: "port|51413|2b"},
},
}
case "2c":
btns = [][]tele.InlineButton{
{
{Text: "◀️ " + tr(uid, "settings_back"), Unique: "fset", Data: "page|2"},
},
{
{Text: "⬇️ " + optBtn("∞", s.DownloadRateLimit == 0), Unique: "fset", Data: "down|0|2c"},
{Text: optBtn("1M", s.DownloadRateLimit == 1024), Unique: "fset", Data: "down|1024|2c"},
{Text: optBtn("5M", s.DownloadRateLimit == 5120), Unique: "fset", Data: "down|5120|2c"},
{Text: optBtn("10M", s.DownloadRateLimit == 10240), Unique: "fset", Data: "down|10240|2c"},
},
{
{Text: "⬆️ " + optBtn("∞", s.UploadRateLimit == 0), Unique: "fset", Data: "up|0|2c"},
{Text: optBtn("1M", s.UploadRateLimit == 1024), Unique: "fset", Data: "up|1024|2c"},
{Text: optBtn("5M", s.UploadRateLimit == 5120), Unique: "fset", Data: "up|5120|2c"},
{Text: optBtn("10M", s.UploadRateLimit == 10240), Unique: "fset", Data: "up|10240|2c"},
},
{
{Text: "🔄 " + optBtn("off", s.RetrackersMode == 0), Unique: "fset", Data: "retr|0|2c"},
{Text: optBtn("add", s.RetrackersMode == 1), Unique: "fset", Data: "retr|1|2c"},
{Text: optBtn("rem", s.RetrackersMode == 2), Unique: "fset", Data: "retr|2|2c"},
{Text: optBtn("repl", s.RetrackersMode == 3), Unique: "fset", Data: "retr|3|2c"},
},
}
case "3":
btns = [][]tele.InlineButton{
{
{Text: "◀️ " + tr(uid, "settings_back"), Unique: "fset", Data: "page|1"},
{Text: "📊 " + tr(uid, "settings_nav_cache"), Unique: "fset", Data: "page|2"},
{Text: "💾 " + tr(uid, "settings_nav_storage"), Unique: "fset", Data: "page|4"},
},
{
{Text: "✏️ " + tr(uid, "settings_set_friendlyname"), Unique: "fset", Data: "ask|friendlyname"},
},
{
{Text: "✏️ " + tr(uid, "settings_set_path"), Unique: "fset", Data: "ask|torrentssavepath"},
},
{
{Text: "🔐 " + tr(uid, "settings_set_sslcert"), Unique: "fset", Data: "ask|sslcert"},
{Text: "🔑 " + tr(uid, "settings_set_sslkey"), Unique: "fset", Data: "ask|sslkey"},
},
{
{Text: "🎬 " + tr(uid, "settings_set_tmdbkey"), Unique: "fset", Data: "ask|tmdbkey"},
},
{
{Text: "🔍 " + tr(uid, "settings_torznab_test"), Unique: "fset", Data: "ask|torznab_test"},
{Text: " " + tr(uid, "settings_add_torznab"), Unique: "fset", Data: "ask|torznab_add"},
{Text: "🗑 " + tr(uid, "settings_clear_torznab"), Unique: "fset", Data: "torznab_clear"},
},
{
{Text: "✏️ " + tr(uid, "settings_set_proxyhosts"), Unique: "fset", Data: "ask|proxyhosts"},
},
}
case "4":
btns = [][]tele.InlineButton{
{
{Text: "◀️ " + tr(uid, "settings_back"), Unique: "fset", Data: "page|1"},
{Text: "📊 " + tr(uid, "settings_nav_cache"), Unique: "fset", Data: "page|2"},
{Text: "✏️ " + tr(uid, "settings_nav_paths"), Unique: "fset", Data: "page|3"},
},
{
{Text: "📄 " + optBtn("json", s.StoreSettingsInJson), Unique: "fset", Data: "storage_set|json"},
{Text: optBtn("bbolt", !s.StoreSettingsInJson), Unique: "fset", Data: "storage_set|bbolt"},
},
{
{Text: "📺 " + optBtn("json", s.StoreViewedInJson), Unique: "fset", Data: "storage_view|json"},
{Text: optBtn("bbolt", !s.StoreViewedInJson), Unique: "fset", Data: "storage_view|bbolt"},
},
{
{Text: "🔄 " + tr(uid, "settings_reset"), Unique: "fset", Data: "reset_confirm"},
},
}
}
return &tele.ReplyMarkup{InlineKeyboard: btns}
}
func boolIcon(v bool) string {
if v {
return "✅"
}
return "❌"
}
func toggleBtn(label string, on bool) string {
if on {
return label + " ✅"
}
return label + " ❌"
}
func optBtn(label string, isCurrent bool) string {
if isCurrent {
return label + " ✓"
}
return label
}
func settingsCallback(c tele.Context, action string) error {
uid := c.Sender().ID
if !isAdmin(uid) {
return c.Respond(&tele.CallbackResponse{Text: tr(uid, "admin_only")})
}
if settings.BTsets == nil {
return c.Respond(&tele.CallbackResponse{Text: tr(uid, "settings_not_loaded")})
}
if action == "export" {
buf, err := json.MarshalIndent(settings.BTsets, "", " ")
if err != nil {
return c.Respond(&tele.CallbackResponse{Text: fmt.Sprintf(tr(uid, "settings_error"), err.Error())})
}
doc := &tele.Document{}
doc.FileName = "torrserver_settings.json"
doc.FileReader = bytes.NewReader(buf)
doc.Caption = "⚙️ " + tr(uid, "settings_export_caption")
_ = c.Send(doc)
return c.Respond(&tele.CallbackResponse{Text: tr(uid, "settings_exported")})
}
if action == "input_cancel" {
return cancelSettingsInput(c)
}
if action == "reset_confirm" {
btnYes := tele.InlineButton{Text: tr(uid, "btn_yes"), Unique: "fset", Data: "reset_def|1"}
btnNo := tele.InlineButton{Text: tr(uid, "btn_no"), Unique: "fset", Data: "reset_def|0"}
kbd := &tele.ReplyMarkup{InlineKeyboard: [][]tele.InlineButton{{btnYes, btnNo}}}
msg := sendSettingsMenuText(c, uid, "4") + "\n\n⚠️ " + tr(uid, "settings_reset_confirm")
if _, err := c.Bot().Edit(c.Callback().Message, msg, kbd, tele.ModeHTML); err != nil {
_ = c.Send(tr(uid, "settings_reset_confirm"), kbd)
}
return c.Respond(&tele.CallbackResponse{})
}
if len(action) > 9 && action[:9] == "reset_def|" {
if action[9:] != "1" {
msg := sendSettingsMenuText(c, uid, "4")
kbd := sendSettingsMenuKbd(uid, "4")
if _, err := c.Bot().Edit(c.Callback().Message, msg, kbd, tele.ModeHTML); err != nil {
_ = sendSettingsMenuPage(c, uid, "4")
}
return c.Respond(&tele.CallbackResponse{Text: tr(uid, "canceled")})
}
if settings.ReadOnly {
return c.Respond(&tele.CallbackResponse{Text: tr(uid, "settings_readonly")})
}
torr.SetDefSettings()
dlna.Stop()
rutor.Stop()
rutor.Start()
msg := sendSettingsMenuText(c, uid, "4")
kbd := sendSettingsMenuKbd(uid, "4")
if _, err := c.Bot().Edit(c.Callback().Message, msg, kbd, tele.ModeHTML); err != nil {
_ = sendSettingsMenuPage(c, uid, "4")
}
return c.Respond(&tele.CallbackResponse{Text: tr(uid, "settings_reset_done")})
}
if len(action) > 12 && action[:12] == "storage_set|" {
if settings.ReadOnly {
return c.Respond(&tele.CallbackResponse{Text: tr(uid, "settings_readonly")})
}
val := action[12:]
prefs := map[string]interface{}{"settings": val}
if err := settings.SetStoragePreferences(prefs); err != nil {
return c.Respond(&tele.CallbackResponse{Text: fmt.Sprintf(tr(uid, "settings_error"), err.Error())})
}
page := "4"
msg := sendSettingsMenuText(c, uid, page)
kbd := sendSettingsMenuKbd(uid, page)
if _, err := c.Bot().Edit(c.Callback().Message, msg, kbd, tele.ModeHTML); err != nil {
_ = sendSettingsMenuPage(c, uid, page)
}
return c.Respond(&tele.CallbackResponse{Text: tr(uid, "settings_saved")})
}
if len(action) > 12 && action[:12] == "storage_view|" {
if settings.ReadOnly {
return c.Respond(&tele.CallbackResponse{Text: tr(uid, "settings_readonly")})
}
val := action[12:]
prefs := map[string]interface{}{"viewed": val}
if err := settings.SetStoragePreferences(prefs); err != nil {
return c.Respond(&tele.CallbackResponse{Text: fmt.Sprintf(tr(uid, "settings_error"), err.Error())})
}
page := "4"
msg := sendSettingsMenuText(c, uid, page)
kbd := sendSettingsMenuKbd(uid, page)
if _, err := c.Bot().Edit(c.Callback().Message, msg, kbd, tele.ModeHTML); err != nil {
_ = sendSettingsMenuPage(c, uid, page)
}
return c.Respond(&tele.CallbackResponse{Text: tr(uid, "settings_saved")})
}
if len(action) > 4 && action[:4] == "ask|" {
setting := action[4:]
var hint string
switch setting {
case "friendlyname":
hint = tr(uid, "settings_hint_friendlyname")
case "torrentssavepath":
hint = tr(uid, "settings_hint_path")
case "sslcert":
hint = tr(uid, "settings_hint_sslcert")
case "sslkey":
hint = tr(uid, "settings_hint_sslkey")
case "tmdbkey":
hint = tr(uid, "settings_hint_tmdbkey")
case "proxyhosts":
hint = tr(uid, "settings_hint_proxyhosts")
case "torznab_add":
hint = tr(uid, "settings_hint_torznab")
case "torznab_test":
hint = tr(uid, "settings_hint_torznab_test")
default:
return c.Respond(&tele.CallbackResponse{Text: tr(uid, "callback_unknown")})
}
return sendSettingsInputPrompt(c, uid, setting, hint)
}
if len(action) > 5 && action[:5] == "page|" {
page := action[5:]
msg := sendSettingsMenuText(c, uid, page)
kbd := sendSettingsMenuKbd(uid, page)
if _, err := c.Bot().Edit(c.Callback().Message, msg, kbd, tele.ModeHTML); err != nil {
_ = sendSettingsMenuPage(c, uid, page)
}
return c.Respond(&tele.CallbackResponse{})
}
if settings.ReadOnly {
return c.Respond(&tele.CallbackResponse{Text: tr(uid, "settings_readonly")})
}
sets := new(settings.BTSets)
*sets = *settings.BTsets
page := "1"
// Extract return page from action (e.g. "rutor|1a" -> action "rutor", page "1a")
if idx := strings.Index(action, "|"); idx >= 0 {
suffix := action[idx+1:]
if suffix == "1a" || suffix == "1b" || suffix == "1c" {
page = suffix
action = action[:idx]
}
}
switch action {
case "rutor":
sets.EnableRutorSearch = !sets.EnableRutorSearch
case "torznab":
sets.EnableTorznabSearch = !sets.EnableTorznabSearch
case "dlna":
sets.EnableDLNA = !sets.EnableDLNA
case "ipv6":
sets.EnableIPv6 = !sets.EnableIPv6
case "upload":
sets.DisableUpload = !sets.DisableUpload
case "dht":
sets.DisableDHT = !sets.DisableDHT
case "pex":
sets.DisablePEX = !sets.DisablePEX
case "tcp":
sets.DisableTCP = !sets.DisableTCP
case "utp":
sets.DisableUTP = !sets.DisableUTP
case "upnp":
sets.DisableUPNP = !sets.DisableUPNP
case "encrypt":
sets.ForceEncrypt = !sets.ForceEncrypt
case "debug":
sets.EnableDebug = !sets.EnableDebug
case "cachedrop":
sets.RemoveCacheOnDrop = !sets.RemoveCacheOnDrop
case "responsive":
sets.ResponsiveMode = !sets.ResponsiveMode
case "proxy":
sets.EnableProxy = !sets.EnableProxy
case "usedisk":
sets.UseDisk = !sets.UseDisk
case "fsactive":
sets.ShowFSActiveTorr = !sets.ShowFSActiveTorr
case "storejson":
sets.StoreSettingsInJson = !sets.StoreSettingsInJson
case "viewedjson":
sets.StoreViewedInJson = !sets.StoreViewedInJson
case "torznab_clear":
if settings.ReadOnly {
return c.Respond(&tele.CallbackResponse{Text: tr(uid, "settings_readonly")})
}
sets.TorznabUrls = nil
page = "3"
torr.SetSettings(sets)
rutor.Stop()
rutor.Start()
msg := sendSettingsMenuText(c, uid, page)
kbd := sendSettingsMenuKbd(uid, page)
if _, err := c.Bot().Edit(c.Callback().Message, msg, kbd, tele.ModeHTML); err != nil {
_ = sendSettingsMenuPage(c, uid, page)
}
return c.Respond(&tele.CallbackResponse{Text: tr(uid, "settings_saved")})
default:
if parts := splitAction(action); len(parts) == 2 {
key, value := parts[0], parts[1]
page = "2"
if idx := strings.Index(value, "|"); idx >= 0 {
if ret := value[idx+1:]; ret == "2a" || ret == "2b" || ret == "2c" {
page = ret
}
value = value[:idx]
}
switch key {
case "cache":
if v := parseInt(value); v > 0 {
sets.CacheSize = int64(v) * 1024 * 1024
}
case "preload":
if v := parseInt(value); v >= 0 && v <= 100 {
sets.PreloadCache = v
}
case "readahead":
if v := parseInt(value); v >= 5 && v <= 100 {
sets.ReaderReadAHead = v
}
case "conn":
if v := parseInt(value); v > 0 {
sets.ConnectionsLimit = v
}
case "timeout":
if v := parseInt(value); v > 0 {
sets.TorrentDisconnectTimeout = v
}
case "port":
v := parseInt(value)
if v >= 0 && (v == 0 || (v >= 1024 && v <= 65535)) {
sets.PeersListenPort = v
}
case "down":
sets.DownloadRateLimit = parseInt(value)
case "up":
sets.UploadRateLimit = parseInt(value)
case "retr":
if v := parseInt(value); v >= 0 && v <= 3 {
sets.RetrackersMode = v
}
default:
return c.Respond(&tele.CallbackResponse{Text: tr(uid, "callback_unknown")})
}
} else {
return c.Respond(&tele.CallbackResponse{Text: tr(uid, "callback_unknown")})
}
}
torr.SetSettings(sets)
dlna.Stop()
if sets.EnableDLNA {
dlna.Start()
}
rutor.Stop()
rutor.Start()
msg := sendSettingsMenuText(c, uid, page)
kbd := sendSettingsMenuKbd(uid, page)
if _, err := c.Bot().Edit(c.Callback().Message, msg, kbd, tele.ModeHTML); err != nil {
_ = sendSettingsMenuPage(c, uid, page)
}
return c.Respond(&tele.CallbackResponse{Text: tr(uid, "settings_saved")})
}
func splitAction(action string) []string {
for i := 0; i < len(action); i++ {
if action[i] == '|' {
return []string{action[:i], action[i+1:]}
}
}
return nil
}
func parseInt(s string) int {
var n int
for _, c := range s {
if c >= '0' && c <= '9' {
n = n*10 + int(c-'0')
}
}
return n
}