Compare commits

...

5 Commits

Author SHA1 Message Date
krau
f9d51a688c chore: update 2024-10-11 00:38:37 +08:00
krau
dcc3f5f99b chore: update example config 2024-10-11 00:08:06 +08:00
krau
a04c46fab8 chore: tidy go.mod 2024-10-11 00:07:34 +08:00
krau
b3c59fde72 feat: webdav storage 2024-10-11 00:07:11 +08:00
krau
d1d284eedc chore: update example config 2024-10-10 23:50:37 +08:00
9 changed files with 130 additions and 24 deletions

View File

@@ -1 +1,27 @@
# Save Any Bot
# Save Any Bot
把 Telegram 的文件保存到各类存储端.
> *就像 PikPak Bot 一样*
## 部署
在 [Release](https://github.com/krau/SaveAny-Bot/releases) 页面下载对应平台的二进制文件.
在解压后目录新建 `config.toml` 文件, 参考 [config.toml.example](https://github.com/krau/SaveAny-Bot/blob/main/config.example.toml) 编辑配置文件.
> [!TIP]
> 由于 Telegram 官方 Bot API 的限制, Bot 无法下载大于 20MB 的文件. 你需要部署一个本地的 Telegram Bot API 来解决这个问题, 然后将配置文件中的 telegram.api 改为你自己的 api 地址.
>
> 参考: [telegram-bot-api-compose](https://github.com/krau/telegram-bot-api-compose)
运行:
```bash
chmod +x saveany-bot
./saveany-bot
```
## 使用
向 Bot 发送(转发)文件, 按照提示操作.

View File

@@ -4,7 +4,7 @@ import (
"fmt"
"regexp"
"github.com/krau/SaveAny-Bot/config"
"github.com/krau/SaveAny-Bot/storage"
"github.com/mymmrac/telego"
"github.com/mymmrac/telego/telegoutil"
)
@@ -20,15 +20,18 @@ func ReplyMessage(replyTo telego.Message, format string, args ...any) (*telego.M
}))
}
var StorageDisplayNames = map[string]string{
"all": "全部",
"local": "服务器磁盘",
"alist": "Alist",
"webdav": "WebDAV",
}
func AddTaskReplyMarkup(messageID int) *telego.InlineKeyboardMarkup {
storageButtons := make([]telego.InlineKeyboardButton, 0)
if config.Cfg.Storage.Local.Enable {
storageButtons = append(storageButtons, telegoutil.InlineKeyboardButton("服务器磁盘").
WithCallbackData(fmt.Sprintf("add %d local", messageID)))
}
if config.Cfg.Storage.Alist.Enable {
storageButtons = append(storageButtons, telegoutil.InlineKeyboardButton("Alist").
WithCallbackData(fmt.Sprintf("add %d alist", messageID)))
for name := range storage.Storages {
storageButtons = append(storageButtons, telegoutil.InlineKeyboardButton(StorageDisplayNames[string(name)]).
WithCallbackData(fmt.Sprintf("add %d %s", messageID, name)))
}
if len(storageButtons) > 1 {

View File

@@ -1,23 +1,34 @@
[telegram]
token = ""
admins = []
token = "" # Bot Token
admins = [777000] # 你的 user_id
api = "https://api.telegram.org"
[log]
level = "DEBUG"
level = "DEBUG" # 日志等级
[temp]
base_path = "cache/"
cache_ttl = 30
base_path = "cache/" # 临时目录, 请不要在此目录下存放任何其他文件
cache_ttl = 30 # 临时文件保存时间, 单位: 秒
[db]
path = "data/data.db" # 数据库文件路径
[storage]
[storage.alist]
[storage.alist] # Alist
enable = true
base_path = "/telegram" # 保存路径
username = "admin" # 用户名
password = "password" # 密码
url = "https://alist.com" # Alist 地址
token_exp = 86400 # token 过期时间, 单位: 秒
[storage.local] # 本地磁盘
enable = true
base_path = "downloads/" # 保存路径
[storage.webdav] # WebDav
enable = true
base_path = "/telegram"
username = "admin"
password = "password"
url = "https://alist.com"
token_exp = 86400
[storage.local]
enable = true
base_path = "downloads/"
url = "https://alist.com/dav"

View File

@@ -37,8 +37,9 @@ type telegramConfig struct {
}
type storageConfig struct {
Alist alistConfig `toml:"alist" mapstructure:"alist"`
Local localConfig `toml:"local" mapstructure:"local"`
Alist alistConfig `toml:"alist" mapstructure:"alist"`
Local localConfig `toml:"local" mapstructure:"local"`
Webdav webdavConfig `toml:"webdav" mapstructure:"webdav"`
}
type alistConfig struct {
@@ -55,6 +56,14 @@ type localConfig struct {
BasePath string `toml:"base_path" mapstructure:"base_path"`
}
type webdavConfig struct {
Enable bool `toml:"enable" mapstructure:"enable"`
URL string `toml:"url" mapstructure:"url"`
Username string `toml:"username" mapstructure:"username"`
Password string `toml:"password" mapstructure:"password"`
BasePath string `toml:"base_path" mapstructure:"base_path"`
}
var Cfg *Config
func Init() {

1
go.mod
View File

@@ -10,6 +10,7 @@ require (
github.com/rhysd/go-github-selfupdate v1.2.3
github.com/spf13/cobra v1.8.1
github.com/spf13/viper v1.19.0
github.com/studio-b12/gowebdav v0.9.0
)
require (

2
go.sum
View File

@@ -158,6 +158,8 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/studio-b12/gowebdav v0.9.0 h1:1j1sc9gQnNxbXXM4M/CebPOX4aXYtr7MojAVcN4dHjU=
github.com/studio-b12/gowebdav v0.9.0/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tcnksm/go-gitconfig v0.1.2 h1:iiDhRitByXAEyjgBqsKi9QU4o2TNtv9kPP3RgPgXBPw=

View File

@@ -9,6 +9,7 @@ import (
"github.com/krau/SaveAny-Bot/logger"
"github.com/krau/SaveAny-Bot/storage/alist"
"github.com/krau/SaveAny-Bot/storage/local"
"github.com/krau/SaveAny-Bot/storage/webdav"
"github.com/krau/SaveAny-Bot/types"
)
@@ -29,6 +30,10 @@ func Init() {
Storages[types.Local] = new(local.Local)
Storages[types.Local].Init()
}
if config.Cfg.Storage.Webdav.Enable {
Storages[types.Webdav] = new(webdav.Webdav)
Storages[types.Webdav].Init()
}
logger.L.Debug("Storage initialized")
}

48
storage/webdav/webdav.go Normal file
View File

@@ -0,0 +1,48 @@
package webdav
import (
"context"
"errors"
"os"
"path/filepath"
"strings"
"github.com/krau/SaveAny-Bot/config"
"github.com/krau/SaveAny-Bot/logger"
"github.com/studio-b12/gowebdav"
)
type Webdav struct{}
var (
Client *gowebdav.Client
basePath string
)
func (w *Webdav) Init() {
webdavConfig := config.Cfg.Storage.Webdav
basePath = strings.TrimSuffix(webdavConfig.BasePath, "/")
Client = gowebdav.NewClient(webdavConfig.URL, webdavConfig.Username, webdavConfig.Password)
if err := Client.Connect(); err != nil {
logger.L.Fatalf("Failed to connect to webdav server: %v", err)
os.Exit(1)
}
}
func (w *Webdav) Save(ctx context.Context, filePath, storagePath string) error {
storagePath = filepath.Join(basePath, storagePath)
if err := Client.MkdirAll(filepath.Dir(storagePath), os.ModePerm); err != nil {
logger.L.Errorf("Failed to create directory %s: %v", filepath.Dir(storagePath), err)
return errors.New("webdav: failed to create directory")
}
fileBytes, err := os.ReadFile(filePath)
if err != nil {
logger.L.Errorf("Failed to read file %s: %v", filePath, err)
return err
}
if err := Client.Write(storagePath, fileBytes, os.ModePerm); err != nil {
logger.L.Errorf("Failed to write file %s: %v", storagePath, err)
return errors.New("webdav: failed to write file")
}
return nil
}

View File

@@ -16,10 +16,11 @@ type StorageType string
var (
StorageAll StorageType = "all"
Local StorageType = "local"
Webdav StorageType = "webdav"
Alist StorageType = "alist"
)
var StorageTypes = []StorageType{Local, Alist, StorageAll}
var StorageTypes = []StorageType{Local, Alist, Webdav, StorageAll}
type Task struct {
Ctx context.Context