Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9d51a688c | ||
|
|
dcc3f5f99b | ||
|
|
a04c46fab8 | ||
|
|
b3c59fde72 | ||
|
|
d1d284eedc |
28
README.md
28
README.md
@@ -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 发送(转发)文件, 按照提示操作.
|
||||
19
bot/utils.go
19
bot/utils.go
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
1
go.mod
@@ -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
2
go.sum
@@ -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=
|
||||
|
||||
@@ -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
48
storage/webdav/webdav.go
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user