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
+218
View File
@@ -0,0 +1,218 @@
package tgbot
import (
"fmt"
"os"
"path/filepath"
"strings"
"sync"
"time"
tele "gopkg.in/telebot.v4"
"server/dlna"
"server/rutor"
"server/settings"
"server/torr"
"server/torznab"
)
const pendingInputTTL = 30 * time.Minute
type pendingInput struct {
Setting string
UserID int64
CreatedAt time.Time
}
var (
pendingInputMu sync.Mutex
pendingInputs = make(map[string]pendingInput)
)
func init() {
go pendingInputCleanup()
}
func pendingInputCleanup() {
ticker := time.NewTicker(5 * time.Minute)
for range ticker.C {
pendingInputMu.Lock()
now := time.Now()
for key, p := range pendingInputs {
if now.Sub(p.CreatedAt) > pendingInputTTL {
delete(pendingInputs, key)
}
}
pendingInputMu.Unlock()
}
}
func sendSettingsInputPrompt(c tele.Context, uid int64, setting, hint string) error {
msg := fmt.Sprintf("✏️ %s\n\n%s", tr(uid, "settings_input_reply"), hint)
btnCancel := tele.InlineButton{Text: "❌ " + tr(uid, "canceled"), Unique: "fset", Data: "input_cancel"}
kbd := &tele.ReplyMarkup{InlineKeyboard: [][]tele.InlineButton{{btnCancel}}}
sent, err := c.Bot().Send(c.Chat(), msg, kbd)
if err != nil {
return err
}
pendingInputMu.Lock()
pendingInputs[chatMsgKey(sent.Chat.ID, sent.ID)] = pendingInput{Setting: setting, UserID: uid, CreatedAt: time.Now()}
pendingInputMu.Unlock()
return c.Respond(&tele.CallbackResponse{})
}
func handleSettingsInputReply(c tele.Context) (handled bool) {
msg := c.Message()
if msg.ReplyTo == nil {
return false
}
key := chatMsgKey(msg.ReplyTo.Chat.ID, msg.ReplyTo.ID)
pendingInputMu.Lock()
pending, ok := pendingInputs[key]
delete(pendingInputs, key)
pendingInputMu.Unlock()
if !ok || pending.UserID != msg.Sender.ID {
return false
}
if time.Since(pending.CreatedAt) > pendingInputTTL {
_ = c.Send(tr(msg.Sender.ID, "canceled"))
return true
}
applySettingsInput(c, pending.Setting, strings.TrimSpace(msg.Text))
return true
}
func cancelSettingsInput(c tele.Context) error {
key := chatMsgKey(c.Callback().Message.Chat.ID, c.Callback().Message.ID)
pendingInputMu.Lock()
delete(pendingInputs, key)
pendingInputMu.Unlock()
_ = c.Bot().Delete(c.Callback().Message)
return c.Respond(&tele.CallbackResponse{Text: tr(c.Sender().ID, "canceled")})
}
func applySettingsInput(c tele.Context, setting, value string) {
uid := c.Sender().ID
if !isAdmin(uid) {
_ = c.Send(tr(uid, "admin_only"))
return
}
if settings.ReadOnly {
_ = c.Send(tr(uid, "settings_readonly"))
return
}
if settings.BTsets == nil {
_ = c.Send(tr(uid, "settings_not_loaded"))
return
}
clear := strings.ToLower(value) == "clear" || strings.ToLower(value) == "очистить" || value == "-"
if clear {
value = ""
}
sets := new(settings.BTSets)
*sets = *settings.BTsets
switch setting {
case "friendlyname":
sets.FriendlyName = value
_ = c.Send(fmt.Sprintf(tr(uid, "settings_input_done"), "FriendlyName", valueOrClear(value)))
case "torrentssavepath":
if value != "" {
abs, err := filepath.Abs(value)
if err != nil {
abs = value
}
if _, err := os.Stat(abs); err != nil && !os.IsNotExist(err) {
_ = c.Send(fmt.Sprintf(tr(uid, "settings_input_error"), err.Error()))
return
}
sets.TorrentsSavePath = abs
sets.UseDisk = true
} else {
sets.TorrentsSavePath = ""
sets.UseDisk = false
}
_ = c.Send(fmt.Sprintf(tr(uid, "settings_input_done"), "TorrentsSavePath", valueOrClear(value)))
case "sslcert":
sets.SslCert = value
_ = c.Send(fmt.Sprintf(tr(uid, "settings_input_done"), "SslCert", valueOrClear(value)))
case "sslkey":
sets.SslKey = value
_ = c.Send(fmt.Sprintf(tr(uid, "settings_input_done"), "SslKey", valueOrClear(value)))
case "tmdbkey":
sets.TMDBSettings.APIKey = value
_ = c.Send(fmt.Sprintf(tr(uid, "settings_input_done"), "TMDB API Key", valueOrClear(value)))
case "proxyhosts":
if value == "" {
sets.ProxyHosts = nil
} else {
parts := strings.Split(value, ",")
for i := range parts {
parts[i] = strings.TrimSpace(parts[i])
}
sets.ProxyHosts = parts
}
_ = c.Send(fmt.Sprintf(tr(uid, "settings_input_done"), "ProxyHosts", valueOrClear(value)))
case "torznab_add":
if value == "" {
_ = c.Send(tr(uid, "settings_input_torznab_usage"))
return
}
parts := strings.SplitN(value, "|", 3)
cfg := settings.TorznabConfig{Host: strings.TrimSpace(parts[0])}
if len(parts) > 1 {
cfg.Key = strings.TrimSpace(parts[1])
}
if len(parts) > 2 {
cfg.Name = strings.TrimSpace(parts[2])
}
if !strings.HasPrefix(cfg.Host, "http") {
cfg.Host = "https://" + cfg.Host
}
sets.TorznabUrls = append(sets.TorznabUrls, cfg)
_ = c.Send(fmt.Sprintf(tr(uid, "settings_input_torznab_added"), cfg.Host))
case "torznab_test":
if value == "" {
_ = c.Send(tr(uid, "settings_input_torznab_usage"))
return
}
parts := strings.SplitN(value, "|", 3)
host := strings.TrimSpace(parts[0])
key := ""
if len(parts) > 1 {
key = strings.TrimSpace(parts[1])
}
if !strings.HasPrefix(host, "http") {
host = "https://" + host
}
if err := torznab.Test(host, key); err != nil {
_ = c.Send(fmt.Sprintf(tr(uid, "settings_torznab_test_fail"), err.Error()))
return
}
_ = c.Send(tr(uid, "settings_torznab_test_ok"))
return
default:
_ = c.Send(tr(uid, "callback_unknown"))
return
}
torr.SetSettings(sets)
dlna.Stop()
if sets.EnableDLNA {
dlna.Start()
}
rutor.Stop()
rutor.Start()
}
func valueOrClear(v string) string {
if v == "" {
return "(cleared)"
}
if len(v) > 50 {
return v[:47] + "..."
}
return v
}