616c6b1c62
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
276 lines
6.8 KiB
Go
276 lines
6.8 KiB
Go
package tgbot
|
|
|
|
import (
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
tele "gopkg.in/telebot.v4"
|
|
"server/dlna"
|
|
"server/rutor"
|
|
"server/settings"
|
|
"server/torr"
|
|
)
|
|
|
|
type pendingPreset struct {
|
|
Sets *settings.BTSets
|
|
Preset string // name for display
|
|
UserID int64
|
|
IsDef bool
|
|
CreatedAt time.Time
|
|
}
|
|
|
|
var (
|
|
pendingPresetMu sync.Mutex
|
|
pendingPresets = make(map[string]pendingPreset)
|
|
)
|
|
|
|
func init() {
|
|
go func() {
|
|
ticker := time.NewTicker(5 * time.Minute)
|
|
for range ticker.C {
|
|
pendingPresetMu.Lock()
|
|
now := time.Now()
|
|
for key, p := range pendingPresets {
|
|
if now.Sub(p.CreatedAt) > 30*time.Minute {
|
|
delete(pendingPresets, key)
|
|
}
|
|
}
|
|
pendingPresetMu.Unlock()
|
|
}
|
|
}()
|
|
}
|
|
|
|
func cmdPreset(c tele.Context) error {
|
|
uid := c.Sender().ID
|
|
if !isAdmin(uid) {
|
|
return c.Send(tr(uid, "admin_only"))
|
|
}
|
|
if settings.BTsets == nil {
|
|
return c.Send(tr(uid, "settings_not_loaded"))
|
|
}
|
|
if settings.ReadOnly {
|
|
return c.Send(tr(uid, "settings_readonly"))
|
|
}
|
|
|
|
args := strings.Fields(c.Text())
|
|
if len(args) < 2 {
|
|
return c.Send(tr(uid, "preset_usage"))
|
|
}
|
|
|
|
sets := new(settings.BTSets)
|
|
*sets = *settings.BTsets
|
|
|
|
first := strings.ToLower(args[1])
|
|
presetName := first
|
|
|
|
if len(args) == 2 {
|
|
if ok, _ := applyNamedPreset(sets, first, uid); ok {
|
|
return sendPresetConfirm(c, uid, sets, presetName, false)
|
|
}
|
|
if first == "default" || first == "def" || first == "сброс" {
|
|
return sendPresetConfirm(c, uid, nil, "default", true)
|
|
}
|
|
}
|
|
|
|
// Parse key-value pairs: cache 256 preload 50 conn 100
|
|
applied, errMsg := applyPresetKV(sets, args[1:], uid)
|
|
if !applied {
|
|
return c.Send(errMsg)
|
|
}
|
|
presetName = strings.Join(args[1:], " ")
|
|
return sendPresetConfirm(c, uid, sets, presetName, false)
|
|
}
|
|
|
|
func sendPresetConfirm(c tele.Context, uid int64, sets *settings.BTSets, presetName string, isDef bool) error {
|
|
btnYes := tele.InlineButton{Text: tr(uid, "btn_yes"), Unique: "fpreset", Data: "1"}
|
|
btnNo := tele.InlineButton{Text: tr(uid, "btn_no"), Unique: "fpreset", Data: "0"}
|
|
kbd := &tele.ReplyMarkup{InlineKeyboard: [][]tele.InlineButton{{btnYes, btnNo}}}
|
|
msg := tr(uid, "preset_confirm") + "\n\n<code>" + presetName + "</code>"
|
|
sent, err := c.Bot().Send(c.Chat(), msg, kbd, tele.ModeHTML)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
pendingPresetMu.Lock()
|
|
pendingPresets[chatMsgKey(sent.Chat.ID, sent.ID)] = pendingPreset{
|
|
Sets: sets, Preset: presetName, UserID: uid, IsDef: isDef, CreatedAt: time.Now(),
|
|
}
|
|
pendingPresetMu.Unlock()
|
|
return nil
|
|
}
|
|
|
|
func presetConfirm(c tele.Context, confirm string) error {
|
|
uid := c.Sender().ID
|
|
if !isAdmin(uid) {
|
|
return c.Respond(&tele.CallbackResponse{Text: tr(uid, "admin_only")})
|
|
}
|
|
key := chatMsgKey(c.Callback().Message.Chat.ID, c.Callback().Message.ID)
|
|
pendingPresetMu.Lock()
|
|
p, ok := pendingPresets[key]
|
|
delete(pendingPresets, key)
|
|
pendingPresetMu.Unlock()
|
|
if !ok || p.UserID != uid {
|
|
_ = c.Bot().Delete(c.Callback().Message)
|
|
return c.Respond(&tele.CallbackResponse{Text: tr(uid, "canceled")})
|
|
}
|
|
if confirm != "1" {
|
|
_ = c.Bot().Delete(c.Callback().Message)
|
|
return c.Respond(&tele.CallbackResponse{Text: tr(uid, "canceled")})
|
|
}
|
|
_ = c.Bot().Delete(c.Callback().Message)
|
|
if settings.ReadOnly {
|
|
return c.Respond(&tele.CallbackResponse{Text: tr(uid, "settings_readonly")})
|
|
}
|
|
if p.IsDef {
|
|
torr.SetDefSettings()
|
|
dlna.Stop()
|
|
rutor.Stop()
|
|
rutor.Start()
|
|
return c.Send(tr(uid, "settings_reset_done"))
|
|
}
|
|
if p.Sets == nil {
|
|
return c.Respond(&tele.CallbackResponse{Text: tr(uid, "callback_unknown")})
|
|
}
|
|
torr.SetSettings(p.Sets)
|
|
dlna.Stop()
|
|
if p.Sets.EnableDLNA {
|
|
dlna.Start()
|
|
}
|
|
rutor.Stop()
|
|
rutor.Start()
|
|
return c.Send(tr(uid, "preset_applied") + p.Preset)
|
|
}
|
|
|
|
func applyNamedPreset(s *settings.BTSets, name string, uid int64) (bool, string) {
|
|
switch name {
|
|
case "performance", "perf", "производительность":
|
|
s.CacheSize = 512 * 1024 * 1024
|
|
s.PreloadCache = 95
|
|
s.ReaderReadAHead = 100
|
|
s.ConnectionsLimit = 100
|
|
s.TorrentDisconnectTimeout = 60
|
|
s.PeersListenPort = 0
|
|
s.DownloadRateLimit = 0
|
|
s.UploadRateLimit = 0
|
|
s.RetrackersMode = 1
|
|
s.ResponsiveMode = true
|
|
return true, tr(uid, "preset_applied") + " performance"
|
|
case "storage", "store", "хранение":
|
|
s.CacheSize = 64 * 1024 * 1024
|
|
s.PreloadCache = 25
|
|
s.ReaderReadAHead = 50
|
|
s.RemoveCacheOnDrop = true
|
|
return true, tr(uid, "preset_applied") + " storage"
|
|
case "streaming", "stream", "стриминг":
|
|
s.CacheSize = 256 * 1024 * 1024
|
|
s.PreloadCache = 75
|
|
s.ReaderReadAHead = 95
|
|
s.ConnectionsLimit = 50
|
|
s.ResponsiveMode = true
|
|
return true, tr(uid, "preset_applied") + " streaming"
|
|
case "low", "minimal", "минимум":
|
|
s.CacheSize = 64 * 1024 * 1024
|
|
s.PreloadCache = 25
|
|
s.ReaderReadAHead = 50
|
|
s.ConnectionsLimit = 25
|
|
s.TorrentDisconnectTimeout = 30
|
|
return true, tr(uid, "preset_applied") + " low"
|
|
case "default", "def", "сброс":
|
|
return false, "" // handled in cmdPreset
|
|
}
|
|
return false, ""
|
|
}
|
|
|
|
func applyPresetKV(s *settings.BTSets, args []string, uid int64) (bool, string) {
|
|
if len(args) < 2 {
|
|
return false, tr(uid, "preset_usage")
|
|
}
|
|
applied := false
|
|
for i := 0; i < len(args)-1; i += 2 {
|
|
key := strings.ToLower(args[i])
|
|
val := strings.ToLower(strings.TrimSpace(args[i+1]))
|
|
ok := false
|
|
switch key {
|
|
case "cache":
|
|
if v := parseInt(val); v > 0 {
|
|
s.CacheSize = int64(v) * 1024 * 1024
|
|
ok = true
|
|
}
|
|
case "preload":
|
|
if v := parseInt(val); v >= 0 && v <= 100 {
|
|
s.PreloadCache = v
|
|
ok = true
|
|
}
|
|
case "readahead":
|
|
if v := parseInt(val); v >= 5 && v <= 100 {
|
|
s.ReaderReadAHead = v
|
|
ok = true
|
|
}
|
|
case "conn", "connections":
|
|
if v := parseInt(val); v > 0 {
|
|
s.ConnectionsLimit = v
|
|
ok = true
|
|
}
|
|
case "timeout":
|
|
if v := parseInt(val); v > 0 {
|
|
s.TorrentDisconnectTimeout = v
|
|
ok = true
|
|
}
|
|
case "port":
|
|
v := parseInt(val)
|
|
if val == "auto" || val == "0" {
|
|
v = 0
|
|
}
|
|
if v >= 0 && (v == 0 || (v >= 1024 && v <= 65535)) {
|
|
s.PeersListenPort = v
|
|
ok = true
|
|
}
|
|
case "down", "download":
|
|
v := 0
|
|
if val != "inf" && val != "∞" && val != "0" {
|
|
v = parseInt(val)
|
|
}
|
|
s.DownloadRateLimit = v
|
|
ok = true
|
|
case "up", "upload":
|
|
v := 0
|
|
if val != "inf" && val != "∞" && val != "0" {
|
|
v = parseInt(val)
|
|
}
|
|
s.UploadRateLimit = v
|
|
ok = true
|
|
case "retr", "retrackers":
|
|
var v int
|
|
switch val {
|
|
case "off":
|
|
v = 0
|
|
case "add":
|
|
v = 1
|
|
case "rem", "remove":
|
|
v = 2
|
|
case "repl", "replace":
|
|
v = 3
|
|
default:
|
|
v = parseInt(val)
|
|
}
|
|
if v >= 0 && v <= 3 {
|
|
s.RetrackersMode = v
|
|
ok = true
|
|
}
|
|
case "responsive":
|
|
s.ResponsiveMode = val == "1" || val == "on" || val == "true" || val == "да" || val == "yes"
|
|
ok = true
|
|
case "cachedrop":
|
|
s.RemoveCacheOnDrop = val == "1" || val == "on" || val == "true" || val == "да" || val == "yes"
|
|
ok = true
|
|
}
|
|
if ok {
|
|
applied = true
|
|
}
|
|
}
|
|
if !applied {
|
|
return false, tr(uid, "preset_usage")
|
|
}
|
|
return true, ""
|
|
}
|